<!DOCTYPE html>
<html lang="en">
    <head>
            <meta charset="utf-8">
            <meta http-equiv="X-UA-Compatible" content="IE=edge">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <meta content="Bmob,BmobCloud,bomb,BaaS,mBaaS,PaaS,Serverless,FaaS,Function as a Service,Backend as a Service,serverless computing,cloud function,后端云,bmob后端云,小程序云,小程序后端,云数据库,云存储,文件存储,云函数,云端代码,定时任务,游戏后端,游戏云,用户系统,无服务器函数,移动开发,app开发,小程序开发,云端一体化,互联网中间件" name="keywords"/>
            <meta content="国内首家专注于移动应用Serverless云服务的平台,Bmob后端云让移动开发更简单,全方位一体化的后端服务平台" name="description"/>
            
            
            
            <link rel="shortcut icon" href="../../../img/favicon.ico">
        
        <script type="text/javascript" src="//game.bmob.cn/static/doc_union.js"></script>
            <!--
            <title>开发文档 - Bmob文档中心</title>
            -->
            <title>数据存储 &middot; Android – Bmob后端云 </title>        
            
            <link href="../../../css/bootstrap-custom.min.css" rel="stylesheet">
            <link href="../../../css/font-awesome-4.5.0.css" rel="stylesheet">
            <link href="../../../css/base.css" rel="stylesheet">
            <link rel="stylesheet" href="../../../css/highlight.css">
            <link href="../../../css/agate.css" rel="stylesheet">
            <link href="../../../css/custom.css" rel="stylesheet" id="custom">
    </head>

    <body >

        <div class="navbar navbar-fixed-top" role="navigation">
    <div class="main-nav">
        <!-- Collapsed navigation -->
        <div class="navbar-header">
            <a class="navbar-brand" href="../../..">
                <!--Bmob文档中心-->
                <img src="../../../img/logo.png" alt="">
            </a>
        </div>

        <!-- Expanded navigation -->
        <div class="navbar-collapse collapse">

          <ul class="nav navbar-nav">
            
              <li class="pull-left">
                
					<a href="../../..">文档首页</a>
                
              </li>
            
              <li class="pull-left active">
                
					<!--<a href="">数据服务</a>-->
					<li class="dropdown active">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">数据服务 <b class="caret"></b></a>
                        <ul class="dropdown-menu">
                            
								<li class="active">
									<a href="../">Android</a>
                                </li>
                            
                            
								<li >
									<a href="../../ios/">iOS</a>
                                </li>
                            
                            
								<li >
									<a href="../../csharp/">C#</a>
                                </li>
                            
                            
								<li >
									<a href="../../php/">PHP</a>
                                </li>
                            
                            
								<li >
									<a href="../../go/">GO</a>
                                </li>
                            
                            
								<li >
									<a href="../../restful/">REST API</a>
                                </li>
                            
                            
								<li >
									<a href="../../wechat_app_new/rm/">JavaScript</a>
                                </li>
                            
                            
                                <li >
                                    <a href="../../cocos2d_x/">Cocos2D-X</a>
                                </li>
							
                            
								<li >
									<a href="../../wechat_app/">小程序</a>
                                </li>
                            
                            
                                <li >
                                    <a href="../../wechat_app_new/">快应用</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../wechat_app_new/">Nodejs</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../wechat_app_new/">Cocos Creator</a>
                                </li>
							
                            
								<li >
									<a href="../../wechat_app_new/rm/">小程序(新)</a>
                                </li>
                            
                            
                                <li >
                                    <a href="../../kotlin/">Kotlin</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../python/">Python</a>
                                </li>
							
                        </ul>
                    </li>
                
              </li>
            
              <li class="pull-left">
                
					<!--<a href="../../../cloud_function/android/">云函数</a>-->
					<li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">云函数 <b class="caret"></b></a>
                        <ul class="dropdown-menu">
                            
                                <li >
                                    <a href="../../../cloud_function/android/">Android</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../cloud_function/ios/">iOS</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../cloud_function/java/">Java</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../cloud_function/python/">Python</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../cloud_function/csharp/">C#</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../cloud_function/php/">PHP</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../cloud_function/javascript/">JavaScript</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../cloud_function/restful/">REST API</a>
                                </li>
							
                            
								<li >
									<a href="../../../cloud_function/web/">Web</a>
                                </li>
                            
                        </ul>
                    </li>
                
              </li>
            
              <li class="pull-left">
                
					<!--<a href="../../../sms/android/">短信服务</a>-->
					<li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">短信服务 <b class="caret"></b></a>
                        <ul class="dropdown-menu">
                            
                                <li >
                                    <a href="../../../sms/android/">Android</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../sms/ios/">iOS</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../sms/php/">PHP</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../sms/javascript/">JavaScript</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../sms/restful/">REST API</a>
                                </li>
							
                        </ul>
                    </li>
                
              </li>
            
              <li class="pull-left">
                
					<!--<a href="">游戏实时后端</a>-->
					<li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">游戏实时后端 <b class="caret"></b></a>
                        <ul class="dropdown-menu">
                            
								<li >
									<a href="../../../game/unity/quick_start/">Unity</a>
                                </li>
                            
                            
								<li >
									<a href="../../../game/cocos_creator/quick_start/">Cocos Creator</a>
                                </li>
                            
                            
								<li >
									<a href="../../../game/wechat_games/quick_start/">微信小游戏</a>
                                </li>
                            
                            
                                <li >
                                    <a href="../../../game/cloud_function/develop_doc/">云函数</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../game/classic_case/">经典案例</a>
                                </li>
							
                        </ul>
                    </li>
                
              </li>
            
              <li class="pull-left">
                
					<!--<a href="">即时通讯</a>-->
					<li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">即时通讯 <b class="caret"></b></a>
                        <ul class="dropdown-menu">
                            
								<li >
									<a href="../../../im/android/">Android</a>
                                </li>
                            
                            
								<li >
									<a href="../../../im/ios/">iOS</a>
                                </li>
                            
                        </ul>
                    </li>
                
              </li>
            
              <li class="pull-left">
                
					<!--<a href="../../../push/android/">推送服务</a>-->
					<li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">推送服务 <b class="caret"></b></a>
                        <ul class="dropdown-menu">
                            
                                <li >
                                    <a href="../../../push/android/">Android</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../push/ios/">iOS</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../push/php/">PHP</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../push/javascript/">JavaScript</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../push/restful/">REST API</a>
                                </li>
							
                        </ul>
                    </li>
                
              </li>
            
              <li class="pull-left">
                
					<!--<a href="../../../other/common_problem/">其他</a>-->
					<li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">其他 <b class="caret"></b></a>
                        <ul class="dropdown-menu">
                            
                                <li >
                                    <a href="../../../other/common_problem/">常见问题</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../other/error_code/">错误码</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../other/data_safety/">数据安全</a>
                                </li>
							
                            
                                <li >
                                    <a href="../../../other/bql/">BQL</a>
                                </li>
							
                        </ul>
                    </li>
                
              </li>
            
              <li>
                  <a href="https://www.bmob.cn/repository/index" target="_blank">知识库</a>
              </li>
              <li>
                  <a href="http://doc.bmob.cn/video/index.html" target="_blank">视频教程</a>
              </li>
            </ul>
            
        </div>
    </div>
</div>

        <div class="pagebody" id="main-wrapper">
            <div class="sidebar">
                <div class="bs-sidebar hidden-print affix well" role="complementary">

	
		
	
		
				
					<div class="code-title">Android</div>
				
				
				
				
				
				
				
				
				
				
				
				
				
				
				
		
	
		
				
				
				
				
				
				
				
				
				
		
	
		
				
				
				
				
				
		
	
		
				
				
				
				
				
		
	
		
				
				
		
	
		
				
				
				
				
				
		
	
		
				
				
				
				
		
	


	<ul class="nav bs-sidenav">
		
			
		
			
				
					
						
							
								
									<li class="">
										<a class="itm-l1" href="../">快速入门</a>
										
									</li>
								
									<li class="active">
										<a class="itm-l1" href="./">开发文档</a>
										
										<ul class="nav">
											
											<li class="active "><a href="#sdk">数据服务SDK</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_1">快速入门</a></li>
												</ul>
												
											</li>
												
											<li class=""><a href="#1">1、数据类型</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#11">1.1、基本数据类型</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#12">1.2、自定义数据类型</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#13">1.3、特殊数据类型</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#14">1.4、自定义数据类型中扩展字段的数据类型</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#15">1.5、自定义数据类型的单条数据操作</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#16">1.6、自定义数据类型的批量数据操作</a></li>
												</ul>
												
											</li>
												
											<li class=""><a href="#2">2、用户系统</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#21">2.1、用户基类</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#22">2.2、用户系统的普通操作</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#23">2.3、用户系统的邮箱操作</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#24">2.4、用户系统的手机号操作</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#25">2.5、用户系统的第三方操作</a></li>
												</ul>
												
											</li>
												
											<li class=""><a href="#3">3、数据关联</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#31">3.1、关联关系描述</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#32">3.2、关联关系案例详解</a></li>
												</ul>
												
											</li>
												
											<li class=""><a href="#4">4、数据查询</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_16">查询条件</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_25">复合查询</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_26">查询结果计数</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_27">查询指定列</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_28">统计查询</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_29">缓存查询</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#bql">BQL查询</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#include">include用法</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_35">内部查询</a></li>
												</ul>
												
											</li>
												
											<li class=""><a href="#5">5、数组操作</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_36">添加数组数据</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_37">更新数组数据</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_38">删除数组数据</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_39">查询数组数据</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_40">类名和表名的关系</a></li>
												</ul>
												
											</li>
												
											<li class=""><a href="#7">7、图文消息</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_44">适用场景</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_45">使用方法</a></li>
												</ul>
												
											</li>
												
											<li class=""><a href="#8">8、文件管理</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_46">创建文件对象</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_47">上传单一文件</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_49">批量上传文件</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_50">下载文件</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_51">删除文件</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_52">批量删除文件</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_53">缩略图</a></li>
												</ul>
												
											</li>
												
											<li class=""><a href="#9">9、数据监听</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_54">开始连接</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_55">监听数据</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_56">取消监听</a></li>
												</ul>
												
											</li>
												
											<li class=""><a href="#10">10、数据安全</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#acl">ACL和角色</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_61">应用安全</a></li>
												</ul>
												
											</li>
												
											<li class=""><a href="#11_1">11、地理位置</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_62">创建地理位置对象</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_63">查询地理位置信息</a></li>
												</ul>
												
											</li>
												
											<li class=""><a href="#12_1">12、其他功能</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_64">获取服务器时间</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_65">自动更新组件</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_66">表结构</a></li>
												</ul>
												
											</li>
												
											<li class=""><a href="#13sdk">13、SDK错误码列表</a>
												
											</li>
												
											<li class=""><a href="#14_1">14、混淆打包</a>
												
											</li>
												
											<li class=""><a href="#15_1">15、其他功能</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_70">模板代码</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_71">重置域名</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_72">数据迁移</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_73">海外加速</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#_74">统计功能</a></li>
												</ul>
												
											</li>
												
											<li class=""><a href="#16_1">16、版本兼容</a>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#android-60">Android 6.0</a></li>
												</ul>
												
												<ul class="nav nav-l2">
													<li><a class="itm-l2" href="#android-p">Android P 网络配置</a></li>
												</ul>
												
											</li>
												
										</ul>
										
									</li>
								
									<li class="">
										<a class="itm-l1" href="../example/">示例/功能</a>
										
									</li>
								
									<li class="">
										<a class="itm-l1" href="../auto_update/">自动更新</a>
										
									</li>
								
									<li class="">
										<a class="itm-l1" href="../class_doc/">类库文档</a>
										
									</li>
								
									<li class="">
										<a class="itm-l1" href="../update_log/">更新日志</a>
										
									</li>
								
							
						
					
						
					
						
					
						
					
						
					
						
					
						
					
						
					
						
					
						
					
						
					
						
					
						
					
						
					
						
					
				
			
		
			
		
			
		
			
		
			
		
			
		
			
		
	</ul>
</div>
            </div>
            <div class="content" role="main">
                <div class="wrap">
                    

<h1 id="sdk">数据服务SDK<a class="headerlink" href="#sdk" title="Permanent link">&para;</a></h1>
<p>Bmob平台为您的移动应用提供了一个完整的后端解决方案，我们提供轻量级的数据服务SDK开发包，让开发者以最少的配置和最简单的方式使用Bmob后端云平台提供的服务，进而完全消除开发者编写服务器代码以及维护服务器的操作。</p>
<h2 id="_1">快速入门<a class="headerlink" href="#_1" title="Permanent link">&para;</a></h2>
<p>建议您在阅读本开发文档之前，先阅读我们提供的 <a href="http://doc.bmob.cn/data/android/">Android快速入门文档</a>，便于您后续的开发。</p>
<h1 id="1">1、数据类型<a class="headerlink" href="#1" title="Permanent link">&para;</a></h1>
<h2 id="11">1.1、基本数据类型<a class="headerlink" href="#11" title="Permanent link">&para;</a></h2>
<p>BmobObject</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>objectId</td>
<td>数据唯一标志</td>
</tr>
<tr>
<td>createdAt</td>
<td>数据创建时间</td>
</tr>
<tr>
<td>updatedAt</td>
<td>数据更新时间</td>
</tr>
<tr>
<td>ACL</td>
<td>数据控制访问权限</td>
</tr>
</tbody>
</table>
<h2 id="12">1.2、自定义数据类型<a class="headerlink" href="#12" title="Permanent link">&para;</a></h2>
<p>所有自定义的数据类型都继承于基本对象类型BmobObject，一个数据对象对应于Bmob控制台一张数据表中的一条数据。</p>
<pre><code>/**
 * Created on 2018/11/22 10:41
 *
 * @author zhangchaozhou
 */
public class Category extends BmobObject {


    /**
     * 类别名称
     */
    private String name;

    /**
     * 类别解释
     */
    private String desc;
    /**
     * 类别排名
     */
    private Integer sequence;


    public String getName() {
        return name;
    }

    public Category setName(String name) {
        this.name = name;
        return this;
    }

    public String getDesc() {
        return desc;
    }

    public Category setDesc(String desc) {
        this.desc = desc;
        return this;
    }

    public Integer getSequence() {
        return sequence;
    }

    public Category setSequence(Integer sequence) {
        this.sequence = sequence;
        return this;
    }
}

</code></pre>

<table>
<thead>
<tr>
<th>继承类</th>
<th>例子</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>自定义类名</td>
<td>Category</td>
<td>对应控制台的表名</td>
</tr>
<tr>
<td>扩展字段名</td>
<td>name</td>
<td>对应控制台该表的字段名</td>
</tr>
</tbody>
</table>
<h2 id="13">1.3、特殊数据类型<a class="headerlink" href="#13" title="Permanent link">&para;</a></h2>
<table>
<thead>
<tr>
<th>类型</th>
<th>解释</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td>BmobUser</td>
<td>对应控制台_User用户表</td>
<td>可以实现用户的注册、登录、短信验证、邮箱验证等功能。</td>
</tr>
<tr>
<td>BmobInstallation</td>
<td>对应控制台_Installation设备表</td>
<td>可以实现将自定义的消息推送给不同的设备终端等操作。</td>
</tr>
<tr>
<td>BmobRole</td>
<td>对应控制台_Role角色表</td>
<td>可以配合ACL进行权限访问控制和角色管理。</td>
</tr>
<tr>
<td>BmobArticle</td>
<td>对应控制台_Article图文消息表</td>
<td>可以进行静态网页加载。</td>
</tr>
</tbody>
</table>
<h2 id="14">1.4、自定义数据类型中扩展字段的数据类型<a class="headerlink" href="#14" title="Permanent link">&para;</a></h2>
<p>Bmob支持的扩展字段数据类型：</p>
<table>
<thead>
<tr>
<th align="left">控制台类型</th>
<th align="left">支持的JAVA类型</th>
<th align="left">说明</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">String</td>
<td align="left">String</td>
<td align="left">字符串类型</td>
</tr>
<tr>
<td align="left">Boolean</td>
<td align="left">Boolean</td>
<td align="left">布尔类型</td>
</tr>
<tr>
<td align="left">Number</td>
<td align="left">Integer、Float、Short、Byte、Double、Long、Character</td>
<td align="left">对应数据库的Number类型，要求是封装类</td>
</tr>
<tr>
<td align="left">Array</td>
<td align="left">List</td>
<td align="left">数组类型</td>
</tr>
<tr>
<td align="left">File</td>
<td align="left">BmobFile</td>
<td align="left">Bmob特有类型，用来标识文件类型</td>
</tr>
<tr>
<td align="left">GeoPoint</td>
<td align="left">BmobGeoPoint</td>
<td align="left">Bmob特有类型，用来标识地理位置</td>
</tr>
<tr>
<td align="left">Date</td>
<td align="left">BmobDate</td>
<td align="left">Bmob特有类型，用来标识日期类型</td>
</tr>
<tr>
<td align="left">Pointer</td>
<td align="left">特定的继承自BmobObject的对象</td>
<td align="left">Bmob特有类型，用来标识指针类型</td>
</tr>
<tr>
<td align="left">Relation</td>
<td align="left">BmobRelation</td>
<td align="left">Bmob特有类型，用来标识数据关联</td>
</tr>
</tbody>
</table>
<h2 id="15">1.5、自定义数据类型的单条数据操作<a class="headerlink" href="#15" title="Permanent link">&para;</a></h2>
<h3 id="151">1.5.1、添加一条数据<a class="headerlink" href="#151" title="Permanent link">&para;</a></h3>
<p>添加一条类别数据：</p>
<pre><code class="java">/**
 * 新增一个对象
 */
private void save() {
    Category category = new Category();
    category.setName(&quot;football&quot;);
    category.setDesc(&quot;足球&quot;);
    category.setSequence(1);
    category.save(new SaveListener&lt;String&gt;() {
        @Override
        public void done(String objectId, BmobException e) {
            if (e == null) {
                mObjectId = objectId;
                Snackbar.make(mBtnSave, &quot;新增成功：&quot; + mObjectId, Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnSave, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h3 id="152">1.5.2、更新数据<a class="headerlink" href="#152" title="Permanent link">&para;</a></h3>
<p>更新一条类别数据，根据objectId来更新：</p>
<pre><code class="java">/**
 * 更新一个对象
 */
private void update() {
    Category category = new Category();
    category.setSequence(2);
    category.update(mObjectId, new UpdateListener() {
        @Override
        public void done(BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnUpdate, &quot;更新成功&quot;, Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnUpdate, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h3 id="153">1.5.3、删除数据<a class="headerlink" href="#153" title="Permanent link">&para;</a></h3>
<p>删除一条类别数据，根据objectId来删除：</p>
<pre><code class="java">/**
 * 删除一个对象
 */
private void delete() {
    Category category = new Category();
    category.delete(mObjectId, new UpdateListener() {
        @Override
        public void done(BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnDelete, &quot;删除成功&quot;, Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnDelete, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}

</code></pre>

<h3 id="154">1.5.4、查询一条数据<a class="headerlink" href="#154" title="Permanent link">&para;</a></h3>
<p>BmobQuery查询一条类别数据，根据objectId来查询：</p>
<pre><code>/**
 * 查询一个对象
 */
private void query() {
    BmobQuery&lt;Category&gt; bmobQuery = new BmobQuery&lt;&gt;();
    bmobQuery.getObject(mObjectId, new QueryListener&lt;Category&gt;() {
        @Override
        public void done(Category category, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnQuery, &quot;查询成功：&quot; + category.getName(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnQuery, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h2 id="16">1.6、自定义数据类型的批量数据操作<a class="headerlink" href="#16" title="Permanent link">&para;</a></h2>
<p>为了提供更稳定的服务，后端启用了QPS限制，推荐采用批量数据操作来替换在循环里多次提交请求的操作，否则会返回QPS达到限制的报错。</p>
<ol>
<li>批量操作每次只支持最大50条记录的操作。</li>
<li>批量操作不支持对User表的操作。</li>
</ol>
<p>BmobBatch：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td>insertBatch</td>
<td>批量添加数据，并返回所添加数据的objectId字段</td>
</tr>
<tr>
<td>updateBatch</td>
<td>批量更新数据</td>
</tr>
<tr>
<td>deleteBatch</td>
<td>批量删除数据</td>
</tr>
<tr>
<td>doBatch</td>
<td>批量添加、批量更新、批量删除同时操作</td>
</tr>
</tbody>
</table>
<h3 id="161">1.6.1、批量添加<a class="headerlink" href="#161" title="Permanent link">&para;</a></h3>
<pre><code class="java">/**
 * 新增多条数据
 */
private void save() {
    List&lt;BmobObject&gt; categories = new ArrayList&lt;&gt;();
    for (int i = 0; i &lt; 3; i++) {
        Category category = new Category();
        category.setName(&quot;category&quot; + i);
        category.setDesc(&quot;类别&quot; + i);
        category.setSequence(i);
        categories.add(category);
    }
    new BmobBatch().insertBatch(categories).doBatch(new QueryListListener&lt;BatchResult&gt;() {

        @Override
        public void done(List&lt;BatchResult&gt; results, BmobException e) {
            if (e == null) {
                for (int i = 0; i &lt; results.size(); i++) {
                    BatchResult result = results.get(i);
                    BmobException ex = result.getError();
                    if (ex == null) {
                        Snackbar.make(mBtnSave, &quot;第&quot; + i + &quot;个数据批量添加成功：&quot; + result.getCreatedAt() + &quot;,&quot; + result.getObjectId() + &quot;,&quot; + result.getUpdatedAt(), Snackbar.LENGTH_LONG).show();
                    } else {
                        Snackbar.make(mBtnSave, &quot;第&quot; + i + &quot;个数据批量添加失败：&quot; + ex.getMessage() + &quot;,&quot; + ex.getErrorCode(), Snackbar.LENGTH_LONG).show();

                    }
                }
            } else {
                Snackbar.make(mBtnSave, &quot;失败：&quot; + e.getMessage() + &quot;,&quot; + e.getErrorCode(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}

</code></pre>

<h3 id="162">1.6.2、批量更新<a class="headerlink" href="#162" title="Permanent link">&para;</a></h3>
<pre><code class="java">/**
 * 更新多条数据
 */
private void update() {

    List&lt;BmobObject&gt; categories = new ArrayList&lt;&gt;();

    Category category = new Category();
    category.setObjectId(&quot;此处填写对应的需要修改数据的objectId&quot;);
    category.setName(&quot;name&quot; + System.currentTimeMillis());
    category.setDesc(&quot;类别&quot; + System.currentTimeMillis());

    Category category1 = new Category();
    category1.setObjectId(&quot;此处填写对应的需要修改数据的objectId&quot;);
    category1.setName(&quot;name&quot; + System.currentTimeMillis());
    category1.setDesc(&quot;类别&quot; + System.currentTimeMillis());

    Category category2 = new Category();
    category2.setObjectId(&quot;此处填写对应的需要修改数据的objectId&quot;);
    category2.setName(&quot;name&quot; + System.currentTimeMillis());
    category2.setDesc(&quot;类别&quot; + System.currentTimeMillis());

    categories.add(category);
    categories.add(category1);
    categories.add(category2);

    new BmobBatch().updateBatch(categories).doBatch(new QueryListListener&lt;BatchResult&gt;() {

        @Override
        public void done(List&lt;BatchResult&gt; results, BmobException e) {
            if (e == null) {
                for (int i = 0; i &lt; results.size(); i++) {
                    BatchResult result = results.get(i);
                    BmobException ex = result.getError();
                    if (ex == null) {
                        Snackbar.make(mBtnUpdate, &quot;第&quot; + i + &quot;个数据批量更新成功：&quot; + result.getCreatedAt() + &quot;,&quot; + result.getObjectId() + &quot;,&quot; + result.getUpdatedAt(), Snackbar.LENGTH_LONG).show();
                    } else {
                        Snackbar.make(mBtnUpdate, &quot;第&quot; + i + &quot;个数据批量更新失败：&quot; + ex.getMessage() + &quot;,&quot; + ex.getErrorCode(), Snackbar.LENGTH_LONG).show();

                    }
                }
            } else {
                Snackbar.make(mBtnUpdate, &quot;失败：&quot; + e.getMessage() + &quot;,&quot; + e.getErrorCode(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}

</code></pre>

<h3 id="163">1.6.3、批量删除<a class="headerlink" href="#163" title="Permanent link">&para;</a></h3>
<pre><code class="Java">/**
 * 删除多条数据
 */
private void delete() {
    List&lt;BmobObject&gt; categories = new ArrayList&lt;&gt;();

    Category category = new Category();
    category.setObjectId(&quot;此处填写对应的需要删除数据的objectId&quot;);

    Category category1 = new Category();
    category1.setObjectId(&quot;此处填写对应的需要删除数据的objectId&quot;);

    Category category2 = new Category();
    category2.setObjectId(&quot;此处填写对应的需要删除数据的objectId&quot;);

    categories.add(category);
    categories.add(category1);
    categories.add(category2);

    new BmobBatch().deleteBatch(categories).doBatch(new QueryListListener&lt;BatchResult&gt;() {

        @Override
        public void done(List&lt;BatchResult&gt; results, BmobException e) {
            if (e == null) {
                for (int i = 0; i &lt; results.size(); i++) {
                    BatchResult result = results.get(i);
                    BmobException ex = result.getError();
                    if (ex == null) {
                        Snackbar.make(mBtnDelete, &quot;第&quot; + i + &quot;个数据批量删除成功：&quot; + result.getCreatedAt() + &quot;,&quot; + result.getObjectId() + &quot;,&quot; + result.getUpdatedAt(), Snackbar.LENGTH_LONG).show();
                    } else {
                        Snackbar.make(mBtnDelete, &quot;第&quot; + i + &quot;个数据批量删除失败：&quot; + ex.getMessage() + &quot;,&quot; + ex.getErrorCode(), Snackbar.LENGTH_LONG).show();

                    }
                }
            } else {
                Snackbar.make(mBtnDelete, &quot;失败：&quot; + e.getMessage() + &quot;,&quot; + e.getErrorCode(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}

</code></pre>

<h3 id="164">1.6.4、批量添加、批量更新、批量删除同时操作<a class="headerlink" href="#164" title="Permanent link">&para;</a></h3>
<pre><code class="Java">/**
 * 同时新增、更新、删除多条数据
 */
private void saveUpdateDelete() {
    BmobBatch batch = new BmobBatch();

    //批量添加
    List&lt;BmobObject&gt; categoriesSave = new ArrayList&lt;&gt;();
    for (int i = 0; i &lt; 3; i++) {
        Category category = new Category();
        category.setName(&quot;category&quot; + i);
        category.setDesc(&quot;类别&quot; + i);
        category.setSequence(i);
        categoriesSave.add(category);
    }


    //批量更新
    List&lt;BmobObject&gt; categoriesUpdate = new ArrayList&lt;&gt;();
    Category categoryUpdate = new Category();
    categoryUpdate.setObjectId(&quot;此处填写对应的需要修改数据的objectId&quot;);
    categoryUpdate.setName(&quot;name&quot; + System.currentTimeMillis());
    categoryUpdate.setDesc(&quot;类别&quot; + System.currentTimeMillis());
    Category categoryUpdate1 = new Category();
    categoryUpdate1.setObjectId(&quot;此处填写对应的需要修改数据的objectId&quot;);
    categoryUpdate1.setName(&quot;name&quot; + System.currentTimeMillis());
    categoryUpdate1.setDesc(&quot;类别&quot; + System.currentTimeMillis());
    Category categoryUpdate2 = new Category();
    categoryUpdate2.setObjectId(&quot;此处填写对应的需要修改数据的objectId&quot;);
    categoryUpdate2.setName(&quot;name&quot; + System.currentTimeMillis());
    categoryUpdate2.setDesc(&quot;类别&quot; + System.currentTimeMillis());
    categoriesUpdate.add(categoryUpdate);
    categoriesUpdate.add(categoryUpdate1);
    categoriesUpdate.add(categoryUpdate2);


    //批量删除
    List&lt;BmobObject&gt; categoriesDelete = new ArrayList&lt;&gt;();
    Category categoryDelete = new Category();
    categoryDelete.setObjectId(&quot;此处填写对应的需要删除数据的objectId&quot;);
    Category categoryDelete1 = new Category();
    categoryDelete1.setObjectId(&quot;此处填写对应的需要删除数据的objectId&quot;);
    Category categoryDelete2 = new Category();
    categoryDelete2.setObjectId(&quot;此处填写对应的需要删除数据的objectId&quot;);
    categoriesDelete.add(categoryDelete);
    categoriesDelete.add(categoryDelete1);
    categoriesDelete.add(categoryDelete2);


    //执行批量操作
    batch.insertBatch(categoriesSave);
    batch.updateBatch(categoriesUpdate);
    batch.deleteBatch(categoriesDelete);
    batch.doBatch(new QueryListListener&lt;BatchResult&gt;() {

        @Override
        public void done(List&lt;BatchResult&gt; results, BmobException e) {
            if (e == null) {
                //返回结果的results和上面提交的顺序是一样的，请一一对应
                for (int i = 0; i &lt; results.size(); i++) {
                    BatchResult result = results.get(i);
                    BmobException ex = result.getError();
                    //只有批量添加才返回objectId
                    if (ex == null) {
                        Snackbar.make(mBtnSaveUpdateDelete, &quot;第&quot; + i + &quot;个数据批量操作成功：&quot; + result.getCreatedAt() + &quot;,&quot; + result.getObjectId() + &quot;,&quot; + result.getUpdatedAt(), Snackbar.LENGTH_LONG).show();
                    } else {
                        Snackbar.make(mBtnSaveUpdateDelete, &quot;第&quot; + i + &quot;个数据批量操作失败：&quot; + ex.getMessage() + &quot;,&quot; + ex.getErrorCode(), Snackbar.LENGTH_LONG).show();
                    }
                }
            } else {
                Snackbar.make(mBtnSaveUpdateDelete, &quot;失败：&quot; + e.getMessage() + &quot;,&quot; + e.getErrorCode(), Snackbar.LENGTH_LONG).show();
            }
        }
    });

}

</code></pre>

<h3 id="165">1.6.5、查询多条数据<a class="headerlink" href="#165" title="Permanent link">&para;</a></h3>
<p>BmobQuery查询多条类别数据：</p>
<pre><code>/**
 * 查询多条数据
 */
private void query() {
    BmobQuery&lt;Category&gt; bmobQuery = new BmobQuery&lt;&gt;();
    bmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; categories, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnQuery, &quot;查询成功：&quot; + categories.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnQuery, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h1 id="2">2、用户系统<a class="headerlink" href="#2" title="Permanent link">&para;</a></h1>
<p>用户基类BmobUser，拥有注册、登录、修改密码、重置密码、短信操作、邮箱操作、第三方操作等功能。</p>
<h2 id="21">2.1、用户基类<a class="headerlink" href="#21" title="Permanent link">&para;</a></h2>
<h3 id="211">2.1.1、默认属性<a class="headerlink" href="#211" title="Permanent link">&para;</a></h3>
<p>BmobUser继承BmobObject，有默认属性：</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>username</td>
<td>用户名/账号/用户唯一标志，可以是邮箱、手机号码、第三方平台的用户唯一标志</td>
</tr>
<tr>
<td>password</td>
<td>用户密码</td>
</tr>
<tr>
<td>email</td>
<td>用户邮箱</td>
</tr>
<tr>
<td>emailVerified</td>
<td>用户邮箱认证状态</td>
</tr>
<tr>
<td>mobilePhoneNumber</td>
<td>用户手机号码</td>
</tr>
<tr>
<td>mobilePhoneNumberVerified</td>
<td>用户手机号码认证状态</td>
</tr>
</tbody>
</table>
<h3 id="212">2.1.2、自定义用户类型<a class="headerlink" href="#212" title="Permanent link">&para;</a></h3>
<p>如果你的用户需要其他属性，如性别、年龄、头像等，则需要继承BmobUser类进行自定义扩展。</p>
<pre><code class="java">/**
 * Created on 2018/11/22 18:01
 *
 * @author zhangchaozhou
 */
public class User extends BmobUser {


    /**
     * 昵称
     */
    private String nickname;

    /**
     * 国家
     */

    private String country;

    /**
     * 得分数
     */
    private Integer score;


    /**
     * 抢断次数
     */
    private Integer steal;


    /**
     * 犯规次数
     */
    private Integer foul;


    /**
     * 失误个数
     */
    private Integer fault;


    /**
     * 年龄
     */
    private Integer age;


    /**
     * 性别
     */
    private Integer gender;


    /**
     * 用户当前位置
     */
    private BmobGeoPoint address;


    /**
     * 头像
     */
    private BmobFile avatar;


    /**
     * 别名
     */
    private List&lt;String&gt; alias;


    public String getNickname() {
        return nickname;
    }

    public User setNickname(String nickname) {
        this.nickname = nickname;
        return this;
    }

    public String getCountry() {
        return country;
    }

    public User setCountry(String country) {
        this.country = country;
        return this;
    }

    public Integer getScore() {
        return score;
    }

    public User setScore(Integer score) {
        this.score = score;
        return this;
    }

    public Integer getSteal() {
        return steal;
    }

    public User setSteal(Integer steal) {
        this.steal = steal;
        return this;
    }

    public Integer getFoul() {
        return foul;
    }

    public User setFoul(Integer foul) {
        this.foul = foul;
        return this;
    }

    public Integer getFault() {
        return fault;
    }

    public User setFault(Integer fault) {
        this.fault = fault;
        return this;
    }

    public Integer getAge() {
        return age;
    }

    public User setAge(Integer age) {
        this.age = age;
        return this;
    }

    public Integer getGender() {
        return gender;
    }

    public User setGender(Integer gender) {
        this.gender = gender;
        return this;
    }

    public BmobGeoPoint getAddress() {
        return address;
    }

    public User setAddress(BmobGeoPoint address) {
        this.address = address;
        return this;
    }

    public BmobFile getAvatar() {
        return avatar;
    }

    public User setAvatar(BmobFile avatar) {
        this.avatar = avatar;
        return this;
    }

    public List&lt;String&gt; getAlias() {
        return alias;
    }

    public User setAlias(List&lt;String&gt; alias) {
        this.alias = alias;
        return this;
    }
}

</code></pre>

<h2 id="22">2.2、用户系统的普通操作<a class="headerlink" href="#22" title="Permanent link">&para;</a></h2>
<h3 id="221">2.2.1、账号密码注册<a class="headerlink" href="#221" title="Permanent link">&para;</a></h3>
<pre><code class="java">/**
 * 账号密码注册
 */
private void signUp(final View view) {
    final User user = new User();
    user.setUsername(&quot;&quot; + System.currentTimeMillis());
    user.setPassword(&quot;&quot; + System.currentTimeMillis());
    user.setAge(18);
    user.setGender(0);
    user.signUp(new SaveListener&lt;User&gt;() {
        @Override
        public void done(User user, BmobException e) {
            if (e == null) {
                Snackbar.make(view, &quot;注册成功&quot;, Snackbar.LENGTH_LONG).show();
            } else {
                Snackbar.make(view, &quot;尚未失败：&quot; + e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h3 id="222">2.2.2、账号密码登录<a class="headerlink" href="#222" title="Permanent link">&para;</a></h3>
<pre><code class="java">/**
 * 账号密码登录
 */
private void login(final View view) {
    final User user = new User();
    //此处替换为你的用户名
    user.setUsername(&quot;username&quot;);
    //此处替换为你的密码
    user.setPassword(&quot;password&quot;);
    user.login(new SaveListener&lt;User&gt;() {
        @Override
        public void done(User bmobUser, BmobException e) {
            if (e == null) {
                User user = BmobUser.getCurrentUser(User.class);
                Snackbar.make(view, &quot;登录成功：&quot; + user.getUsername(), Snackbar.LENGTH_LONG).show();
            } else {
                Snackbar.make(view, &quot;登录失败：&quot; + e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code class="java">/**
 * 账号密码登录
 */
private void loginByAccount(final View view) {
    //此处替换为你的用户名密码
    BmobUser.loginByAccount(&quot;username&quot;, &quot;password&quot;, new LogInListener&lt;User&gt;() {
        @Override
        public void done(User user, BmobException e) {
            if (e == null) {
                Snackbar.make(view, &quot;登录成功：&quot; + user.getUsername(), Snackbar.LENGTH_LONG).show();
            } else {
                Snackbar.make(view, &quot;登录失败：&quot; + e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h3 id="223">2.2.3、判断当前是否有用户登录<a class="headerlink" href="#223" title="Permanent link">&para;</a></h3>
<pre><code class="java">if (BmobUser.isLogin()) {
    User user = BmobUser.getCurrentUser(User.class);
    Snackbar.make(view, &quot;已经登录：&quot; + user.getUsername(), Snackbar.LENGTH_LONG).show();
} else {
    Snackbar.make(view, &quot;尚未登录&quot;, Snackbar.LENGTH_LONG).show();
}
</code></pre>

<h3 id="224">2.2.4、获取当前用户以及用户属性<a class="headerlink" href="#224" title="Permanent link">&para;</a></h3>
<p>获取缓存的用户信息，缓存的有效期为1年。</p>
<pre><code class="java">if (BmobUser.isLogin()) {
    User user = BmobUser.getCurrentUser(User.class);
    Snackbar.make(view, &quot;当前用户：&quot; + user.getUsername() + &quot;-&quot; + user.getAge(), Snackbar.LENGTH_LONG).show();
    String username = (String) BmobUser.getObjectByKey(&quot;username&quot;);
    Integer age = (Integer) BmobUser.getObjectByKey(&quot;age&quot;);
    Snackbar.make(view, &quot;当前用户属性：&quot; + username + &quot;-&quot; + age, Snackbar.LENGTH_LONG).show();
} else {
    Snackbar.make(view, &quot;尚未登录，请先登录&quot;, Snackbar.LENGTH_LONG).show();
}
</code></pre>

<h3 id="225">2.2.5、同步本地缓存的用户信息<a class="headerlink" href="#225" title="Permanent link">&para;</a></h3>
<p>同步控制台数据到缓存中：</p>
<pre><code class="java">
   /**
 * 同步控制台数据到缓存中
 * @param view
 */
private void fetchUserInfo(final View view) {
    BmobUser.fetchUserInfo(new FetchUserInfoListener&lt;BmobUser&gt;() {
        @Override
        public void done(BmobUser user, BmobException e) {
            if (e == null) {
                final MyUser myUser = BmobUser.getCurrentUser(MyUser.class);
                Snackbar.make(view, &quot;更新用户本地缓存信息成功：&quot;+myUser.getUsername()+&quot;-&quot;+myUser.getAge(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;error&quot;,e.getMessage());
                Snackbar.make(view, &quot;更新用户本地缓存信息失败：&quot; + e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<p>获取控制台最新JSON数据，不同步到缓存中：</p>
<pre><code class="Java">/**
 * 获取控制台最新JSON数据
 * @param view
 */
private void fetchUserJsonInfo(final View view) {
    BmobUser.fetchUserJsonInfo(new FetchUserInfoListener&lt;String&gt;() {
        @Override
        public void done(String json, BmobException e) {
            if (e == null) {
                Log.e(&quot;success&quot;,json);
                Snackbar.make(view, &quot;获取控制台最新数据成功：&quot;+json, Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;error&quot;,e.getMessage());
                Snackbar.make(view, &quot;获取控制台最新数据失败：&quot; + e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}

</code></pre>

<h3 id="226">2.2.6、更新用户信息<a class="headerlink" href="#226" title="Permanent link">&para;</a></h3>
<p>在更新用户信息时，如果用户邮箱有变更并且在管理后台打开了邮箱验证选项的话，Bmob云后端同样会自动发一封邮件验证信息给用户。</p>
<pre><code class="java">/**
 * 更新用户操作并同步更新本地的用户信息
 */
private void updateUser(final View view) {
    final User user = BmobUser.getCurrentUser(User.class);
    user.setAge(20);
    user.update(new UpdateListener() {
        @Override
        public void done(BmobException e) {
            if (e == null) {
                Snackbar.make(view, &quot;更新用户信息成功：&quot; + user.getAge(), Snackbar.LENGTH_LONG).show();
            } else {
                Snackbar.make(view, &quot;更新用户信息失败：&quot; + e.getMessage(), Snackbar.LENGTH_LONG).show();
                Log.e(&quot;error&quot;, e.getMessage());
            }
        }
    });
}

</code></pre>

<h3 id="227">2.2.7、查询用户<a class="headerlink" href="#227" title="Permanent link">&para;</a></h3>
<p>查询用户和查询普通对象一样，只需指定BmobUser类或自定义用户类即可，如下：</p>
<pre><code class="java">/**
 * 查询用户表
 */
private void queryUser(final View view) {
    BmobQuery&lt;User&gt; bmobQuery = new BmobQuery&lt;&gt;();
    bmobQuery.findObjects(new FindListener&lt;User&gt;() {
        @Override
        public void done(List&lt;User&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(view, &quot;查询成功&quot;, Snackbar.LENGTH_LONG).show();
            } else {
                Snackbar.make(view, &quot;查询失败：&quot; + e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}

</code></pre>

<h3 id="228">2.2.8、提供旧密码修改密码<a class="headerlink" href="#228" title="Permanent link">&para;</a></h3>
<p>若用户已登录，可以提供旧密码修改密码：</p>
<pre><code class="java">/**
 * 提供旧密码修改密码
 */
private void updatePassword(final View view){
    //TODO 此处替换为你的旧密码和新密码
    BmobUser.updateCurrentUserPassword(&quot;oldPwd&quot;, &quot;newPwd&quot;, new UpdateListener() {
        @Override
        public void done(BmobException e) {
            if (e == null) {
                Snackbar.make(view, &quot;查询成功&quot;, Snackbar.LENGTH_LONG).show();
            } else {
                Snackbar.make(view, &quot;查询失败：&quot; + e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h3 id="229">2.2.9、退出登录<a class="headerlink" href="#229" title="Permanent link">&para;</a></h3>
<p>退出登录，同时清除缓存用户对象。</p>
<pre><code class="java">BmobUser.logOut();
</code></pre>

<h2 id="23">2.3、用户系统的邮箱操作<a class="headerlink" href="#23" title="Permanent link">&para;</a></h2>
<p>需在应用的设置-&gt;邮件设置中开启“邮箱验证”功能，Bmob云后端才会在邮箱注册时发出一封验证邮件给用户，默认开启。</p>
<p>邮件功能是按需付费，可以在应用的设置-&gt;套餐升级中购买邮件的数量。</p>
<h3 id="231">2.3.1、邮箱密码登录<a class="headerlink" href="#231" title="Permanent link">&para;</a></h3>
<p>邮箱验证通过后，用户可以使用邮箱和密码进行登录：</p>
<pre><code class="java">/**
 * 邮箱+密码登录
 */
private void loginByEmailPwd() {
    //TODO 此处替换为你的邮箱和密码
    BmobUser.loginByAccount(&quot;email&quot;,&quot;password&quot;, new LogInListener&lt;User&gt;() {

        @Override
        public void done(User user, BmobException e) {
            if (e == null) {
                Snackbar.make(mIvAvatar, user.getUsername() + &quot;-&quot; + user.getAge() + &quot;-&quot; + user.getObjectId() + &quot;-&quot; + user.getEmail(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mIvAvatar, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h3 id="232">2.3.2、邮箱验证<a class="headerlink" href="#232" title="Permanent link">&para;</a></h3>
<p>邮件验证功能会在用户(User)对象中加入emailVerified字段，当一个用户的邮件被新添加或者修改过的话，emailVerified会被默认设为false，如果应用设置中开启了邮箱认证功能，Bmob会对用户填写的邮箱发送一个链接，这个链接可以把emailVerified设置为true.</p>
<p>emailVerified 字段有 3 种状态可以考虑：</p>
<table>
<thead>
<tr>
<th>状态</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>true</td>
<td>已验证。</td>
</tr>
<tr>
<td>false</td>
<td>未验证，可以刷新用户对象更新此状态为最新状态。</td>
</tr>
<tr>
<td>missing</td>
<td>用户对象已经被创建，但应用设置并没有开启邮件验证功能；或者用户对象没有email邮箱。</td>
</tr>
</tbody>
</table>
<h3 id="233">2.3.3、发送邮箱验证邮件<a class="headerlink" href="#233" title="Permanent link">&para;</a></h3>
<p>发送给用户的邮箱验证邮件会在一周内失效：</p>
<pre><code class="java">/**
 * 发送验证邮件
 */
private void emailVerify() {
    //TODO 此处替换为你的邮箱
    final String email = &quot;email&quot;;
    BmobUser.requestEmailVerify(email, new UpdateListener() {

        @Override
        public void done(BmobException e) {
            if (e == null) {
                Snackbar.make(mIvAvatar, &quot;请求验证邮件成功，请到&quot; + email + &quot;邮箱中进行激活账户。&quot;, Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mIvAvatar, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h3 id="234">2.3.4、邮箱重置密码<a class="headerlink" href="#234" title="Permanent link">&para;</a></h3>
<p>开发者只需要求用户输入注册时的电子邮件地址即可：</p>
<pre><code class="java">/**
 * 邮箱重置密码
 */
private void resetPasswordByEmail() {
    //TODO 此处替换为你的邮箱
    final String email = &quot;email&quot;;
    BmobUser.resetPasswordByEmail(email, new UpdateListener() {

        @Override
        public void done(BmobException e) {
            if (e == null) {
                Snackbar.make(mIvAvatar, &quot;重置密码请求成功，请到&quot; + email + &quot;邮箱进行密码重置操作&quot;, Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mIvAvatar, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<p>邮箱重置密码的流程如下：</p>
<ol>
<li>用户输入他们的电子邮件，请求重置自己的密码。</li>
<li>Bmob向他们的邮箱发送一封包含特殊的密码重置链接的电子邮件。</li>
<li>用户根据向导点击重置密码连接，打开一个特殊的Bmob页面，根据提示他们可以输入一个新的密码。</li>
<li>用户的密码已被重置为新输入的密码。</li>
</ol>
<h2 id="24">2.4、用户系统的手机号操作<a class="headerlink" href="#24" title="Permanent link">&para;</a></h2>
<h3 id="241">2.4.1、手机号码和密码登录<a class="headerlink" href="#241" title="Permanent link">&para;</a></h3>
<p>在手机号码被验证后，用户可以使用该手机号码和密码进行登录操作：</p>
<pre><code class="java">/**
 * 手机号码密码登录
 */
private void loginByPhone(){
    //TODO 此处替换为你的手机号码和密码
    BmobUser.loginByAccount(&quot;phone&quot;, &quot;password&quot;, new LogInListener&lt;User&gt;() {

        @Override
        public void done(User user, BmobException e) {
            if(user!=null){
                if (e == null) {
                    mTvInfo.append(&quot;短信登录成功：&quot; + user.getObjectId() + &quot;-&quot; + user.getUsername());
                } else {
                    mTvInfo.append(&quot;短信登录失败：&quot; + e.getErrorCode() + &quot;-&quot; + e.getMessage() + &quot;\n&quot;);
                }
            }
        }
    });
}
</code></pre>

<h3 id="242">2.4.2、手机号码和短信验证码登录<a class="headerlink" href="#242" title="Permanent link">&para;</a></h3>
<p>在手机号码被验证后，用户可以使用该手机号码和短信验证码进行登录操作：</p>
<p>1、先请求登录操作的短信验证码，使用方式详见<a href="http://doc.bmob.cn/sms/android/">短信开发文档</a>：</p>
<pre><code class="java">/**
 * TODO template 如果是自定义短信模板，此处替换为你在控制台设置的自定义短信模板名称；如果没有对应的自定义短信模板，则使用默认短信模板，默认模板名称为空字符串&quot;&quot;。
 *
 * TODO 应用名称以及自定义短信内容，请使用不会被和谐的文字，防止发送短信验证码失败。
 *
 */
BmobSMS.requestSMSCode(phone, &quot;&quot;, new QueryListener&lt;Integer&gt;() {
    @Override
    public void done(Integer smsId, BmobException e) {
        if (e == null) {
            mTvInfo.append(&quot;发送验证码成功，短信ID：&quot; + smsId + &quot;\n&quot;);
        } else {
            mTvInfo.append(&quot;发送验证码失败：&quot; + e.getErrorCode() + &quot;-&quot; + e.getMessage() + &quot;\n&quot;);
        }
    }
});
</code></pre>

<p>2、然后进行手机号码和短信验证码登录:</p>
<pre><code class="java">/**
 * TODO 此API需要在用户已经注册并验证的前提下才能使用
 */
BmobUser.loginBySMSCode(phone, code, new LogInListener&lt;BmobUser&gt;() {
    @Override
    public void done(BmobUser bmobUser, BmobException e) {
        if (e == null) {
            mTvInfo.append(&quot;短信登录成功：&quot; + bmobUser.getObjectId() + &quot;-&quot; + bmobUser.getUsername());
            startActivity(new Intent(UserLoginSmsActivity.this, UserMainActivity.class));
        } else {
            mTvInfo.append(&quot;短信登录失败：&quot; + e.getErrorCode() + &quot;-&quot; + e.getMessage() + &quot;\n&quot;);
        }
    }
});
</code></pre>

<h3 id="243">2.4.3、手机号码一键注册或登录<a class="headerlink" href="#243" title="Permanent link">&para;</a></h3>
<p>手机号码一键注册或登录：</p>
<p>1、先请求登录或注册操作的短信验证码：</p>
<pre><code class="java">/**
 * TODO template 如果是自定义短信模板，此处替换为你在控制台设置的自定义短信模板名称；如果没有对应的自定义短信模板，则使用默认短信模板，默认模板名称为空字符串&quot;&quot;。
 *
 * TODO 应用名称以及自定义短信内容，请使用不会被和谐的文字，防止发送短信验证码失败。
 *
 */
BmobSMS.requestSMSCode(phone, &quot;&quot;, new QueryListener&lt;Integer&gt;() {
    @Override
    public void done(Integer smsId, BmobException e) {
        if (e == null) {
            mTvInfo.append(&quot;发送验证码成功，短信ID：&quot; + smsId + &quot;\n&quot;);
        } else {
            mTvInfo.append(&quot;发送验证码失败：&quot; + e.getErrorCode() + &quot;-&quot; + e.getMessage() + &quot;\n&quot;);
        }
    }
});
</code></pre>

<p>2、然后进行一键注册或登录:</p>
<pre><code class="java">BmobUser.signOrLoginByMobilePhone(phone, code, new LogInListener&lt;BmobUser&gt;() {
    @Override
    public void done(BmobUser bmobUser, BmobException e) {
        if (e == null) {
            mTvInfo.append(&quot;短信注册或登录成功：&quot; + bmobUser.getUsername());
            startActivity(new Intent(UserSignUpOrLoginSmsActivity.this, UserMainActivity.class));
        } else {
            mTvInfo.append(&quot;短信注册或登录失败：&quot; + e.getErrorCode() + &quot;-&quot; + e.getMessage() + &quot;\n&quot;);
        }
    }
});

</code></pre>

<p>3、如果想在一键注册或登录的同时保存其他字段的数据：</p>
<pre><code class="java">/**
 * 一键注册或登录的同时保存其他字段的数据
 * @param phone
 * @param code
 */
private void signOrLogin(String phone,String code) {
    User user = new User();
    //设置手机号码（必填）
    user.setMobilePhoneNumber(phone);
    //设置用户名，如果没有传用户名，则默认为手机号码
    user.setUsername(phone);
    //设置用户密码
    user.setPassword(&quot;&quot;);
    //设置额外信息：此处为年龄
    user.setAge(18);
    user.signOrLogin(code, new SaveListener&lt;MyUser&gt;() {

        @Override
        public void done(MyUser user,BmobException e) {
            if (e == null) {
                mTvInfo.append(&quot;短信注册或登录成功：&quot; + user.getUsername());
                startActivity(new Intent(UserSignUpOrLoginSmsActivity.this, UserMainActivity.class));
            } else {
                mTvInfo.append(&quot;短信注册或登录失败：&quot; + e.getErrorCode() + &quot;-&quot; + e.getMessage() + &quot;\n&quot;);
            }
        }
    });
}
</code></pre>

<h3 id="244">2.4.4、绑定/解绑手机号码<a class="headerlink" href="#244" title="Permanent link">&para;</a></h3>
<p>如果已有用户系统，需要为用户绑定/解绑手机号：</p>
<p>1、发送短信验证码：</p>
<pre><code>/**
 * TODO template 如果是自定义短信模板，此处替换为你在控制台设置的自定义短信模板名称；如果没有对应的自定义短信模板，则使用默认短信模板，默认模板使用空字符串&quot;&quot;。
 */
BmobSMS.requestSMSCode(phoneInput, &quot;DataSDK&quot;, new QueryListener&lt;Integer&gt;() {
    @Override
    public void done(Integer smsId, BmobException e) {
        if (e == null) {
            mTvInfo.append(&quot;发送验证码成功，短信ID：&quot; + smsId + &quot;\n&quot;);
        } else {
            mTvInfo.append(&quot;发送验证码失败：&quot; + e.getErrorCode() + &quot;-&quot; + e.getMessage() + &quot;\n&quot;);
        }
    }
});
</code></pre>

<p>2、验证短信验证码，验证成功后更新用户的手机号码和手机号码的验证状态：</p>
<pre><code>BmobSMS.verifySmsCode(phone, code, new UpdateListener() {
    @Override
    public void done(BmobException e) {
        if (e == null) {
            mTvInfo.append(&quot;验证码验证成功，您可以在此时进行绑定操作！\n&quot;);
            User user = BmobUser.getCurrentUser(User.class);
            user.setMobilePhoneNumber(phone);
            //绑定
            user.setMobilePhoneNumberVerified(true);
            //解绑
            //user.setMobilePhoneNumberVerified(false);
            user.update(new UpdateListener() {
                @Override
                public void done(BmobException e) {
                    if (e == null) {
                        mTvInfo.append(&quot;绑定/解绑手机号码成功&quot;);
                    } else {
                        mTvInfo.append(&quot;绑定/解绑手机号码失败：&quot; + e.getErrorCode() + &quot;-&quot; + e.getMessage());
                    }
                }
            });
        } else {
            mTvInfo.append(&quot;验证码验证失败：&quot; + e.getErrorCode() + &quot;-&quot; + e.getMessage() + &quot;\n&quot;);
        }
    }
});

</code></pre>

<h3 id="245">2.4.5、手机号码重置密码<a class="headerlink" href="#245" title="Permanent link">&para;</a></h3>
<p>如果用户已经验证过手机号码或者使用过手机号码注册或登录，也可以通过手机号码来重置用户密码:</p>
<p>1、请求重置密码操作的短信验证码：</p>
<pre><code class="java">/**
 * TODO template 如果是自定义短信模板，此处替换为你在控制台设置的自定义短信模板名称；如果没有对应的自定义短信模板，则使用默认短信模板，模板名称为空字符串&quot;&quot;。
 */
BmobSMS.requestSMSCode(phone, &quot;DataSDK&quot;, new QueryListener&lt;Integer&gt;() {
    @Override
    public void done(Integer smsId, BmobException e) {
        if (e == null) {
            mTvInfo.append(&quot;发送验证码成功，短信ID：&quot; + smsId + &quot;\n&quot;);
        } else {
            mTvInfo.append(&quot;发送验证码失败：&quot; + e.getErrorCode() + &quot;-&quot; + e.getMessage() + &quot;\n&quot;);
        }
    }
});
</code></pre>

<p>2、然后执行验证码的密码重置操作：</p>
<pre><code class="java">BmobUser.resetPasswordBySMSCode(code, newPassword, new UpdateListener() {
    @Override
    public void done(BmobException e) {
        if (e == null) {
            mTvInfo.append(&quot;重置成功&quot;);
        } else {
            mTvInfo.append(&quot;重置失败：&quot; + e.getErrorCode() + &quot;-&quot; + e.getMessage());
        }
    }
});
</code></pre>

<h2 id="25">2.5、用户系统的第三方操作<a class="headerlink" href="#25" title="Permanent link">&para;</a></h2>
<p>Bmob提供了第三方平台登陆的功能，目前支持<code>新浪微博</code>、<code>QQ账号</code>、<code>微信账号</code>的登陆，此功能与第三方开放平台的SDK解藕。</p>
<p>BmobThirdUserAuth的各参数解释：</p>
<table>
<thead>
<tr>
<th>参数</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>snsType</td>
<td>固定值：weibo或qq或weixin</td>
</tr>
<tr>
<td>accessToken</td>
<td>接口调用凭证，由第三方平台返回取得</td>
</tr>
<tr>
<td>expiresIn</td>
<td>access_token的有效时间，由第三方平台返回取得</td>
</tr>
<tr>
<td>userId</td>
<td>用户身份的唯一标识，由第三方平台返回取得，对应微博授权信息中的uid，对应qq和微信授权信息中的openid</td>
</tr>
</tbody>
</table>
<h3 id="251">2.5.1、应用场景<a class="headerlink" href="#251" title="Permanent link">&para;</a></h3>
<p>第三方账号登陆目前适应以下两种应用场景：</p>
<p><code>一、没有Bmob账号，希望使用第三方账号一键注册或登陆Bmob账号</code></p>
<p>如果开发者希望用户使用第三方平台的账号注册或登录Bmob的用户体系，则推荐的步骤如下：</p>
<p>1、第三方平台授权，开发者需自行根据第三方平台文档提出的授权方法完成账号授权并得到授权信息</p>
<p>2、调用Bmob提供的<code>loginWithAuthData（BmobV3.3.9版本提供）</code>方法，并自行构造<code>BmobThirdUserAuth（第三方授权信息）</code>对象，调用成功后，在Bmob的User表中会产生一条记录。</p>
<p><code>二、已有Bmob账号，希望与第三方账号进行关联</code></p>
<p>如果已使用Bmob的用户体系（假设用户A已登录），希望和第三方平台进行关联，则推荐的步骤如下：</p>
<p>1、第三方平台授权，开发者需自行根据第三方平台文档提出的授权方法完成账号授权并得到授权信息</p>
<p>2、调用<code>associateWithAuthData</code>方法，并自行构造<code>BmobThirdUserAuth(第三方授权信息)</code>对象，调用成功后，你就会在后台的用户A的authData这个字段下面看到提交的授权信息。</p>
<h3 id="252">2.5.2、第三方平台一键注册或登录<a class="headerlink" href="#252" title="Permanent link">&para;</a></h3>
<p>假设你已通过上述提供的文档完成相应平台的授权并得到对应的授权信息，则可以这样来完成一键注册或登陆操作：</p>
<pre><code class="java">
/**
 * 第三方平台一键注册或登录
 * @param snsType
 * @param accessToken
 * @param expiresIn
 * @param userId
 */
private void thirdSingupLogin(String snsType, String accessToken, String expiresIn, String userId) {
    BmobUser.BmobThirdUserAuth authInfo = new BmobUser.BmobThirdUserAuth(snsType, accessToken, expiresIn, userId);
    BmobUser.loginWithAuthData(authInfo, new LogInListener&lt;JSONObject&gt;() {
        @Override
        public void done(JSONObject user, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnThirdSignupLogin, &quot;第三方平台一键注册或登录成功&quot;, Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnThirdSignupLogin, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h3 id="253">2.5.3、关联第三方平台<a class="headerlink" href="#253" title="Permanent link">&para;</a></h3>
<pre><code class="java">/**
 * 第三方平台关联
 * @param snsType
 * @param accessToken
 * @param expiresIn
 * @param userId
 */
private void associate(String snsType, String accessToken, String expiresIn, String userId){
    BmobUser.BmobThirdUserAuth authInfo = new BmobUser.BmobThirdUserAuth(snsType,accessToken, expiresIn, userId);
    BmobUser.associateWithAuthData(authInfo, new UpdateListener() {

        @Override
        public void done(BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnThirdSignupLogin, &quot;第三方平台关联成功&quot;, Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnThirdSignupLogin, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}

</code></pre>

<h3 id="254">2.5.4、解除第三方平台关联<a class="headerlink" href="#254" title="Permanent link">&para;</a></h3>
<pre><code class="java">
/**
 * 取消第三方平台关联
 * @param snsType
 */
private void unAssociate(String snsType) {
    BmobUser.dissociateAuthData(snsType,new UpdateListener() {

        @Override
        public void done(BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnThirdSignupLogin, &quot;第三方平台关联成功&quot;, Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                if (e.getErrorCode()==208){
                    Snackbar.make(mBtnThirdSignupLogin, &quot;你没有关联该账号&quot;, Snackbar.LENGTH_LONG).show();
                }else {
                    Snackbar.make(mBtnThirdSignupLogin, e.getMessage(), Snackbar.LENGTH_LONG).show();
                }
            }
        }
    });
}
</code></pre>

<h3 id="255">2.5.5、微博登陆相关文档<a class="headerlink" href="#255" title="Permanent link">&para;</a></h3>
<p>1、<a href="http://open.weibo.com/wiki/%E7%A7%BB%E5%8A%A8%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%8E%A5%E5%85%A5">移动客户端接入文档</a>：此文档请着重查阅其中的<code>SDK接入流程</code>。</p>
<p>2、<a href="https://github.com/sinaweibosdk/weibo_android_sdk">新浪微博AndroidSDK快速入门</a>，请详细查看<code>README</code>文档,其介绍了完整的集成流程。</p>
<p>3、<a href="https://github.com/sinaweibosdk/weibo_android_sdk/blob/master/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%20FAQ.md#5-%E5%85%B3%E4%BA%8E%E6%8E%88%E6%9D%83">新浪微博常见问题</a>：在新浪微博授权过程中出现问题，请查看此文档，一般出现频率较高的错误有：</p>
<p><code>sso package and sign error</code>- 平台上填写的包名和签名不正确。请仔细检查，一般最需要检查的是<code>签名</code>，签名需要使用微博提供的获取签名的工具<a href="https://github.com/sinaweibosdk/weibo_android_sdk/blob/master/app_signatures.apk">（app_signatures.apk）</a>。</p>
<p><code>redirect_uri_mismatch</code>     - 请确保你在weibo平台上填写的授权回调地址与代码中写的授权回调地址(RedirectURI)一样。</p>
<h3 id="256qq">2.5.6、QQ登陆相关文档<a class="headerlink" href="#256qq" title="Permanent link">&para;</a></h3>
<p>1、如何使用SDK，请参见 <a href="http://wiki.open.qq.com/wiki/mobile/Android_SDK%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E">腾讯开放平台Android_SDK使用说明</a>。</p>
<p>2、如何调用具体API，请参见 <a href="http://wiki.open.qq.com/wiki/Android_API%E8%B0%83%E7%94%A8%E8%AF%B4%E6%98%8E">API调用说明</a>。</p>
<p>3、常见问题汇总，请参见<a href="http://bbs.open.qq.com/thread-6159767-1-1.html">问题汇总说明</a>。</p>
<h3 id="257">2.5.7、微信登陆相关文档<a class="headerlink" href="#257" title="Permanent link">&para;</a></h3>
<p>1、<a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&amp;t=resource/res_list&amp;verify=1&amp;lang=zh_CN&amp;token=a6350e5290b2fee66bf0a98f02d7ddc7a655ddce">Android接入指南</a>：这里主要介绍的是微信sdk的集成步骤</p>
<p>2、<a href="https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&amp;t=resource/res_list&amp;verify=1&amp;lang=zh_CN&amp;token=a6350e5290b2fee66bf0a98f02d7ddc7a655ddce">微信登陆开发指南</a>：在<code>移动应用开发</code>-&gt;<code>微信登录功能</code>-&gt;<code>移动应用微信登录开发指南</code>。主要介绍微信OAuth2.0授权登录的流程。</p>
<h1 id="3">3、数据关联<a class="headerlink" href="#3" title="Permanent link">&para;</a></h1>
<h2 id="31">3.1、关联关系描述<a class="headerlink" href="#31" title="Permanent link">&para;</a></h2>
<p>在程序设计中，不同类型的数据之间可能存在某种关系。
比如：帖子和作者的关系，一篇帖子只属于某个作者，这是<code>一对一的关系</code>。
比如：帖子和评论的关系，一条评论只属于某一篇帖子，而一篇帖子对应有很多条评论，这是<code>一对多的关系</code>。
比如：学生和课程的关系，一个学生可以选择很多课程，一个课程也可以被很多学生所选择，这是<code>多对多的关系</code>。</p>
<p>Bmob提供了<code>Pointer（一对一、一对多）</code>和<code>Relation（多对多）</code>两种数据类型来解决这种业务需求。</p>
<h2 id="32">3.2、关联关系案例详解<a class="headerlink" href="#32" title="Permanent link">&para;</a></h2>
<p>由于关联关系讲解起来比较复杂，以下用一个简单的案例来说明在Bmob中是如何使用关联关系的。</p>
<p>用户发表帖子，同时又可对帖子进行评论留言，在这个场景中涉及到三个表：用户表（<code>_User</code>）、帖子表（<code>Post</code>）、评论表（<code>Comment</code>）,以下是各个表的字段：</p>
<p><code>_User</code>字段如下：</p>
<table>
<thead>
<tr>
<th align="left">字段</th>
<th align="left">类型</th>
<th align="left">含义</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">objectId</td>
<td align="left">String</td>
<td align="left">用户ID</td>
</tr>
<tr>
<td align="left">username</td>
<td align="left">String</td>
<td align="left">用户名(可以既发帖子又发评论)</td>
</tr>
<tr>
<td align="left">age</td>
<td align="left">Integer</td>
<td align="left">年龄</td>
</tr>
</tbody>
</table>
<p><code>Post</code>字段如下：</p>
<table>
<thead>
<tr>
<th align="left">字段</th>
<th align="left">类型</th>
<th align="left">含义</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">objectId</td>
<td align="left">String</td>
<td align="left">帖子ID</td>
</tr>
<tr>
<td align="left">title</td>
<td align="left">String</td>
<td align="left">帖子标题</td>
</tr>
<tr>
<td align="left">content</td>
<td align="left">String</td>
<td align="left">帖子内容</td>
</tr>
<tr>
<td align="left">author</td>
<td align="left">Pointer</td>
<td align="left">帖子作者</td>
</tr>
<tr>
<td align="left">likes</td>
<td align="left">Relation</td>
<td align="left">喜欢帖子的读者</td>
</tr>
</tbody>
</table>
<p><code>Comment</code>字段如下：</p>
<table>
<thead>
<tr>
<th align="left">字段</th>
<th align="left">类型</th>
<th align="left">含义</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">objectId</td>
<td align="left">String</td>
<td align="left">评论ID</td>
</tr>
<tr>
<td align="left">content</td>
<td align="left">String</td>
<td align="left">评论内容</td>
</tr>
<tr>
<td align="left">post</td>
<td align="left">Pointer</td>
<td align="left">评论对应的帖子</td>
</tr>
<tr>
<td align="left">author</td>
<td align="left">Pointer</td>
<td align="left">评论该帖子的人</td>
</tr>
</tbody>
</table>
<h4 id="web">Web端创建关联字段<a class="headerlink" href="#web" title="Permanent link">&para;</a></h4>
<p>如果你需要在Web端创建上述表的话，那么当选择的字段类型为<code>Pointer或Relation</code>时，会提示你选择该字段所指向或关联的数据表。</p>
<p>如下图所示：</p>
<p><img alt="图1 创建关联字段" src="../image/createline.png" /></p>
<h4 id="_2">创建数据对象<a class="headerlink" href="#_2" title="Permanent link">&para;</a></h4>
<pre><code class="java">
/**
 * @author zhangchaozhou
 */
public class Post extends BmobObject {

    /**
     * 帖子标题
     */
    private String title;

    /**
     * 帖子内容
     */
    private String content;

    /**
     * 发布者
     */
    private User author;
    /**
     * 图片
     */
    private BmobFile image;

    /**
     * 一对多关系：用于存储喜欢该帖子的所有用户
     */
    private BmobRelation likes;


    public String getTitle() {
        return title;
    }

    public Post setTitle(String title) {
        this.title = title;
        return this;
    }

    public String getContent() {
        return content;
    }

    public Post setContent(String content) {
        this.content = content;
        return this;
    }

    public User getAuthor() {
        return author;
    }

    public Post setAuthor(User author) {
        this.author = author;
        return this;
    }

    public BmobFile getImage() {
        return image;
    }

    public Post setImage(BmobFile image) {
        this.image = image;
        return this;
    }

    public BmobRelation getLikes() {
        return likes;
    }

    public Post setLikes(BmobRelation likes) {
        this.likes = likes;
        return this;
    }
}


</code></pre>

<pre><code class="java">/**
 * @author zhangchaozhou
 */
public class Comment extends BmobObject {

    /**
     * 评论内容
     */
    private String content;

    /**
     * 评论的用户
     */
    private User user;

    /**
     * 所评论的帖子
     */
    private Post post;


    public String getContent() {
        return content;
    }

    public Comment setContent(String content) {
        this.content = content;
        return this;
    }

    public User getUser() {
        return user;
    }

    public Comment setUser(User user) {
        this.user = user;
        return this;
    }

    public Post getPost() {
        return post;
    }

    public Comment setPost(Post post) {
        this.post = post;
        return this;
    }
}
</code></pre>

<p><strong>注：</strong></p>
<p><strong>1、类名要和数据表名保持一致。</strong></p>
<p><strong>2、MyUser属性对应为Pointer的指针类型。</strong></p>
<p>以下举例均假定A用户已注册并登陆</p>
<p><img alt="图1" src="../image/userA.png" /></p>
<h3 id="_3">一对一关系<a class="headerlink" href="#_3" title="Permanent link">&para;</a></h3>
<p><strong>用户发表帖子，一篇帖子也只能属于某个用户，那么帖子和用户之间的关系是<code>一对一关系</code>，建议使用<code>Pointer</code>类型来表示。</strong></p>
<p><code>Pointer</code>本质上可以看成是我们将一个指向某条记录的指针记录下来，我们查询时可以通过该指针来获得其指向的关联对象。</p>
<p>用户A写了一篇帖子，需要在<code>Post</code>表中生成一条记录，并将该帖子关联到用户A这条记录，表明该帖子是A所发表的。</p>
<p>示例如下：</p>
<h4 id="_4">添加一对一关联<a class="headerlink" href="#_4" title="Permanent link">&para;</a></h4>
<pre><code class="java">/**
 * 添加一对一关联，当前用户发布帖子
 */
private void savePost() {
    if (BmobUser.isLogin()){
        Post post = new Post();
        post.setTitle(&quot;帖子标题&quot;);
        post.setContent(&quot;帖子内容&quot;);
        //添加一对一关联，用户关联帖子
        post.setAuthor(BmobUser.getCurrentUser(User.class));
        post.save(new SaveListener&lt;String&gt;() {
            @Override
            public void done(String s, BmobException e) {
                if (e == null) {
                    Snackbar.make(mFabAddPost, &quot;发布帖子成功：&quot; + s, Snackbar.LENGTH_LONG).show();
                } else {
                    Log.e(&quot;BMOB&quot;, e.toString());
                    Snackbar.make(mFabAddPost, e.getMessage(), Snackbar.LENGTH_LONG).show();
                }
            }
        });
    }else {
        Snackbar.make(mFabAddPost, &quot;请先登录&quot;, Snackbar.LENGTH_LONG).show();
    }
}
</code></pre>

<h4 id="_5">查询一对一关联<a class="headerlink" href="#_5" title="Permanent link">&para;</a></h4>
<p>查询当前用户所发表的所有帖子：</p>
<pre><code class="java">/**
 * 查询一对一关联，查询当前用户发表的所有帖子
 */
private void queryPostAuthor() {

    if (BmobUser.isLogin()) {
        BmobQuery&lt;Post&gt; query = new BmobQuery&lt;&gt;();
        query.addWhereEqualTo(&quot;author&quot;, BmobUser.getCurrentUser(User.class));
        query.order(&quot;-updatedAt&quot;);
        //包含作者信息
        query.include(&quot;author&quot;);
        query.findObjects(new FindListener&lt;Post&gt;() {

            @Override
            public void done(List&lt;Post&gt; object, BmobException e) {
                if (e == null) {
                    Snackbar.make(mFabAddPost, &quot;查询成功&quot;, Snackbar.LENGTH_LONG).show();
                } else {
                    Log.e(&quot;BMOB&quot;, e.toString());
                    Snackbar.make(mFabAddPost, e.getMessage(), Snackbar.LENGTH_LONG).show();
                }
            }

        });
    } else {
        Snackbar.make(mFabAddPost, &quot;请先登录&quot;, Snackbar.LENGTH_LONG).show();
    }

</code></pre>

<h4 id="_6">更新一对一关联<a class="headerlink" href="#_6" title="Permanent link">&para;</a></h4>
<p>将某帖子的作者修改成其他用户：</p>
<pre><code class="java">/**
 * 修改一对一关联，修改帖子和用户的关系
 */
private void updatePostAuthor() {
    User user = new User();
    user.setObjectId(&quot;此处填写你需要关联的用户&quot;);
    Post post = new Post();
    post.setObjectId(&quot;此处填写需要修改的帖子&quot;);
    //修改一对一关联，修改帖子和用户的关系
    post.setAuthor(user);
    post.update(new UpdateListener() {
        @Override
        public void done(BmobException e) {
            if (e == null) {
                Snackbar.make(mFabAddPost, &quot;修改帖子成功&quot;, Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mFabAddPost, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h4 id="_7">删除一对一关联<a class="headerlink" href="#_7" title="Permanent link">&para;</a></h4>
<p>如果你想和<code>ESIt3334</code>这个帖子解除关联关系，可以这样：</p>
<pre><code class="java">/**
 * 删除一对一关联，解除帖子和用户的关系
 */
private void removePostAuthor() {
    Post post = new Post();
    post.setObjectId(&quot;此处填写需要修改的帖子&quot;);
    //删除一对一关联，解除帖子和用户的关系
    post.remove(&quot;author&quot;);
    post.update(new UpdateListener() {
        @Override
        public void done(BmobException e) {
            if (e == null) {
                Snackbar.make(mFabAddPost, &quot;修改帖子成功&quot;, Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mFabAddPost, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<p>删除成功后，在后台的<code>Post</code>表中，你就会看到<code>ESIt3334</code>这个帖子的<code>author</code>字段的值已经被置空了。</p>
<p><img alt="图1" src="../image/postdelete.png" /></p>
<h3 id="_8">一对多关系<a class="headerlink" href="#_8" title="Permanent link">&para;</a></h3>
<p><strong>一条评论只能属于某一篇帖子，一篇帖子可以有很多用户对其进行评论，那么帖子和评论之间的关系就是<code>一对多关系</code>，推荐使用<code>pointer</code>类型来表示</strong>。</p>
<p>因为使用方法和上面的一对一关联基本相同，只是查询一对多关联的时候有些区别，故只举添加和查询两个例子：</p>
<h4 id="_9">添加一对多关联<a class="headerlink" href="#_9" title="Permanent link">&para;</a></h4>
<p>将评论和微博进行关联，并同时和当前用户进行关联，表明是当前用户对该帖子进行评论，示例如下：</p>
<pre><code class="java">MyUser user = BmobUser.getCurrentUser(MyUser.class);
Post post = new Post();
post.setObjectId(&quot;ESIt3334&quot;);
final Comment comment = new Comment();
comment.setContent(content);
comment.setPost(post);
comment.setUser(user);
comment.save(new SaveListener&lt;String&gt;() {

    @Override
    public void done(String objectId,BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;评论发表成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage());
        }
    }

});

</code></pre>

<h4 id="_10">查询一对多关联<a class="headerlink" href="#_10" title="Permanent link">&para;</a></h4>
<p>我想<code>查询出某个帖子（objectId为ESIt3334）的所有评论,同时将该评论的作者的信息也查询出来</code>，那么可以使用<code>addWhereEqualTo</code>方法进行查询：</p>
<pre><code class="java">BmobQuery&lt;Comment&gt; query = new BmobQuery&lt;Comment&gt;();
//用此方式可以构造一个BmobPointer对象。只需要设置objectId就行
Post post = new Post();
post.setObjectId(&quot;ESIt3334&quot;);
query.addWhereEqualTo(&quot;post&quot;,new BmobPointer(post));        
//希望同时查询该评论的发布者的信息，以及该帖子的作者的信息，这里用到上面`include`的并列对象查询和内嵌对象的查询
query.include(&quot;user,post.author&quot;);
query.findObjects(new FindListener&lt;Comment&gt;() {

    @Override
    public void done(List&lt;Comment&gt; objects,BmobException e) {
        ...
    }
});

</code></pre>

<p>注：<code>addWhereEqualTo</code>对<code>BmobPonter</code>类型的一对多的关联查询是<code>BmobSDKV3.3.8</code>开始支持的，因此使用时，请更新SDK版本。</p>
<h3 id="_11">多对多关系<a class="headerlink" href="#_11" title="Permanent link">&para;</a></h3>
<p><strong>一个帖子可以被很多用户所喜欢，一个用户也可能会喜欢很多帖子，那么可以使用<code>Relation</code>类型来表示这种<code>多对多关联关系</code></strong>。</p>
<p><code>Relation</code>本质上可以理解为其存储的是一个对象，而这个对象中存储的是多个指向其它记录的指针。</p>
<h4 id="_12">添加多对多关联<a class="headerlink" href="#_12" title="Permanent link">&para;</a></h4>
<pre><code class="java">MyUser user = BmobUser.getCurrentUser(MyUser.class);
Post post = new Post();
post.setObjectId(&quot;ESIt3334&quot;);
//将当前用户添加到Post表中的likes字段值中，表明当前用户喜欢该帖子
BmobRelation relation = new BmobRelation();
//将当前用户添加到多对多关联中
relation.add(user);
//多对多关联指向`post`的`likes`字段
post.setLikes(relation);
post.update(new UpdateListener() {
    @Override
    public void done(BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;多对多关联添加成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage());
        }
    }

});

</code></pre>

<p>添加成功后，在后台的<code>Post</code>表中就能查看到<code>likes</code>字段已经生成并对应到了<code>_User</code></p>
<p><img alt="图1" src="../image/relation.png" /></p>
<p>点击红框中的<code>关联关系</code>按钮展开后，可查看刚才所添加的喜欢该帖子的用户A：</p>
<p><img alt="图1" src="../image/likes.png" /></p>
<h4 id="_13">查询多对多关联<a class="headerlink" href="#_13" title="Permanent link">&para;</a></h4>
<p>如果希望<code>查询喜欢该帖子（objectId为ESIt3334）的所有用户</code>,那么就需要用到<code>addWhereRelatedTo</code>方法进行多对多关联查询。</p>
<p>示例代码：</p>
<pre><code class="java">// 查询喜欢这个帖子的所有用户，因此查询的是用户表
BmobQuery&lt;MyUser&gt; query = new BmobQuery&lt;MyUser&gt;();
Post post = new Post();
post.setObjectId(&quot;ESIt3334&quot;);
//likes是Post表中的字段，用来存储所有喜欢该帖子的用户
query.addWhereRelatedTo(&quot;likes&quot;, new BmobPointer(post));
query.findObjects(new FindListener&lt;MyUser&gt;() {

    @Override
    public void done(List&lt;MyUser&gt; object,BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;查询个数：&quot;+object.size());
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage());
        }
    }

});

</code></pre>

<h4 id="_14">修改多对多关联<a class="headerlink" href="#_14" title="Permanent link">&para;</a></h4>
<p>如果<code>用户B也喜欢该帖子（objectId为ESIt3334）</code>，此时需要为该帖子(Post)的<code>likes</code>字段多添加一个用户,示例如下：</p>
<pre><code class="java">Post post = new Post();
post.setObjectId(&quot;ESIt3334&quot;);
//将用户B添加到Post表中的likes字段值中，表明用户B喜欢该帖子
BmobRelation relation = new BmobRelation();
//构造用户B
MyUser user = new MyUser();
user.setObjectId(&quot;aJyG2224&quot;);
//将用户B添加到多对多关联中
relation.add(user);
//多对多关联指向`post`的`likes`字段
post.setLikes(relation);
post.update(new UpdateListener() {

    @Override
    public void done(BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;用户B和该帖子关联成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage());
        }
    }

});

</code></pre>

<p>修改成功后，你在点击该帖子的<code>likes</code>字段下面的<code>关联关系</code>按钮展开后，可查看刚才所添加的喜欢该帖子的用户B：</p>
<p><img alt="图1" src="../image/updaterelation.png" /></p>
<h4 id="_15">删除多对多关联<a class="headerlink" href="#_15" title="Permanent link">&para;</a></h4>
<p>如果<code>想对该帖子进行取消喜欢的操作</code>，此时，需要删除之前的多对多关联，具体代码：</p>
<pre><code class="java">Post post = new Post();
post.setObjectId(&quot;83ce274594&quot;);
MyUser user = BmobUser.getCurrentUser(MyUser.class);
BmobRelation relation = new BmobRelation();
relation.remove(user);
post.setLikes(relation);
post.update(new UpdateListener() {

    @Override
    public void done(BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;关联关系删除成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage());
        }
    }

});

</code></pre>

<p><strong>1 例子中的Comment和Post表请大家注意下在后端控制台建表的数据类型是Pointer还是Relation 否则返回类型不匹配的111错误，表的结构和字段类型如下：</strong>
<img alt="Post" src="http://i.imgur.com/o4giGoy.png" />
<img alt="Comment" src="http://i.imgur.com/RmsP7m8.png" />
<strong>2 为方便大家了解学习，我们提供了一个关于数据关联的Demo，下载地址是：https://github.com/bmob/RelationDemo</strong></p>
<h1 id="4">4、数据查询<a class="headerlink" href="#4" title="Permanent link">&para;</a></h1>
<p>数据的查询可能是每个应用都会频繁使用到的，BmobSDK中提供了<code>BmobQuery</code>类，它提供了多样的方法来实现不同条件的查询，同时它的使用也是非常的简单和方便的。</p>
<p><strong>注：</strong>
2 v3.5.2开始可以对查询条件等提供链式调用的写法，如下：</br></p>
<pre><code class="java">BmobQuery&lt;Book&gt; query = new BmobQuery&lt;&gt;();
        query.setLimit(8).setSkip(1).order(&quot;-createdAt&quot;)
                .findObjects(new FindListener&lt;Book&gt;() {
                    @Override
                    public void done(List&lt;Book&gt; object, BmobException e) {
                        if (e == null) {
                            // ...
                        } else {
                            // ...
                        }
                    }
                });
</code></pre>

<h3 id="_16">查询条件<a class="headerlink" href="#_16" title="Permanent link">&para;</a></h3>
<p>在查询的使用过程中，基于不同条件的查询是非常常见的，BmobQuery同样也支持不同条件的查询。</p>
<h4 id="_17">比较查询<a class="headerlink" href="#_17" title="Permanent link">&para;</a></h4>
<table>
<thead>
<tr>
<th>方法</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td>addWhereEqualTo</td>
<td>等于</td>
</tr>
<tr>
<td>addWhereNotEqualTo</td>
<td>不等于</td>
</tr>
<tr>
<td>addWhereLessThan</td>
<td>小于</td>
</tr>
<tr>
<td>addWhereLessThanOrEqualTo</td>
<td>小于等于</td>
</tr>
<tr>
<td>addWhereGreaterThan</td>
<td>大于</td>
</tr>
<tr>
<td>addWhereGreaterThanOrEqualTo</td>
<td>大于等于</td>
</tr>
</tbody>
</table>
<pre><code>/**
 * name为football的类别
 */
private void equal() {
    BmobQuery&lt;Category&gt; categoryBmobQuery = new BmobQuery&lt;&gt;();
    categoryBmobQuery.addWhereEqualTo(&quot;name&quot;, &quot;football&quot;);
    categoryBmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnEqual, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnEqual, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code>/**
 * name不为football的类别
 */
private void notEqual() {
    BmobQuery&lt;Category&gt; categoryBmobQuery = new BmobQuery&lt;&gt;();
    categoryBmobQuery.addWhereNotEqualTo(&quot;name&quot;, &quot;football&quot;);
    categoryBmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnEqual, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnEqual, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code>/**
 * sequence小于10的类别
 */
private void less() {
    BmobQuery&lt;Category&gt; categoryBmobQuery = new BmobQuery&lt;&gt;();
    categoryBmobQuery.addWhereLessThan(&quot;sequence&quot;, 10);
    categoryBmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnEqual, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnEqual, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code>/**
 * sequence小于等于10的类别
 */
private void lessEqual() {
    BmobQuery&lt;Category&gt; categoryBmobQuery = new BmobQuery&lt;&gt;();
    categoryBmobQuery.addWhereLessThanOrEqualTo(&quot;sequence&quot;, 10);
    categoryBmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnEqual, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnEqual, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code>/**
 * sequence大于10的类别
 */
private void large() {
    BmobQuery&lt;Category&gt; categoryBmobQuery = new BmobQuery&lt;&gt;();
    categoryBmobQuery.addWhereGreaterThan(&quot;sequence&quot;, 10);
    categoryBmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnEqual, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnEqual, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code>/**
 * sequence大于等于10的类别
 */
private void largeEqual() {
    BmobQuery&lt;Category&gt; categoryBmobQuery = new BmobQuery&lt;&gt;();
    categoryBmobQuery.addWhereGreaterThanOrEqualTo(&quot;sequence&quot;, 10);
    categoryBmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnEqual, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnEqual, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h4 id="_18">子查询<a class="headerlink" href="#_18" title="Permanent link">&para;</a></h4>
<p>如果你想查询匹配几个不同值的数据，如：要查询“Barbie”,“Joe”,“Julia”三个人的成绩时，你可以使用<code>addWhereContainedIn</code>方法来实现。</p>
<pre><code class="java">String[] names = {&quot;Barbie&quot;, &quot;Joe&quot;, &quot;Julia&quot;};
query.addWhereContainedIn(&quot;playerName&quot;, Arrays.asList(names));
</code></pre>

<p>相反，如果你想查询排除“Barbie”,“Joe”,“Julia”这三个人的其他同学的信息，你可以使用<code>addWhereNotContainedIn</code>方法来实现。</p>
<pre><code class="java">String[] names = {&quot;Barbie&quot;, &quot;Joe&quot;, &quot;Julia&quot;};
query.addWhereNotContainedIn(&quot;playerName&quot;, Arrays.asList(names));
</code></pre>

<h4 id="_19">时间查询<a class="headerlink" href="#_19" title="Permanent link">&para;</a></h4>
<pre><code class="java">/**
 * 某个时间
 */
private void equal() throws ParseException {
    String createdAt = &quot;2018-11-23 10:30:00&quot;;
    SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);
    Date createdAtDate = sdf.parse(createdAt);
    BmobDate bmobCreatedAtDate = new BmobDate(createdAtDate);


    BmobQuery&lt;Category&gt; categoryBmobQuery = new BmobQuery&lt;&gt;();
    categoryBmobQuery.addWhereEqualTo(&quot;createdAt&quot;, bmobCreatedAtDate);
    categoryBmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnEqual, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnEqual, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code class="java">/**
 * 某个时间外
 */
private void notEqual() throws ParseException {
    String createdAt = &quot;2018-11-23 10:30:00&quot;;
    SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);
    Date createdAtDate = sdf.parse(createdAt);
    BmobDate bmobCreatedAtDate = new BmobDate(createdAtDate);


    BmobQuery&lt;Category&gt; categoryBmobQuery = new BmobQuery&lt;&gt;();
    categoryBmobQuery.addWhereNotEqualTo(&quot;createdAt&quot;, bmobCreatedAtDate);
    categoryBmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnEqual, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnEqual, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code class="java">/**
 * 某个时间前
 */
private void less() throws ParseException {
    String createdAt = &quot;2018-11-23 10:30:00&quot;;
    SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);
    Date createdAtDate = sdf.parse(createdAt);
    BmobDate bmobCreatedAtDate = new BmobDate(createdAtDate);


    BmobQuery&lt;Category&gt; categoryBmobQuery = new BmobQuery&lt;&gt;();
    categoryBmobQuery.addWhereLessThan(&quot;createdAt&quot;, bmobCreatedAtDate);
    categoryBmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnEqual, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnEqual, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code class="java">/**
 * 某个时间及以前
 */
private void lessEqual() throws ParseException {
    String createdAt = &quot;2018-11-23 10:30:00&quot;;
    SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);
    Date createdAtDate = sdf.parse(createdAt);
    BmobDate bmobCreatedAtDate = new BmobDate(createdAtDate);


    BmobQuery&lt;Category&gt; categoryBmobQuery = new BmobQuery&lt;&gt;();
    categoryBmobQuery.addWhereLessThanOrEqualTo(&quot;createdAt&quot;, bmobCreatedAtDate);
    categoryBmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnEqual, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnEqual, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code class="java">/**
 * 某个时间后
 */
private void large() throws ParseException {
    String createdAt = &quot;2018-11-23 10:30:00&quot;;
    SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);
    Date createdAtDate = sdf.parse(createdAt);
    BmobDate bmobCreatedAtDate = new BmobDate(createdAtDate);


    BmobQuery&lt;Category&gt; categoryBmobQuery = new BmobQuery&lt;&gt;();
    categoryBmobQuery.addWhereGreaterThan(&quot;createdAt&quot;, bmobCreatedAtDate);
    categoryBmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnEqual, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnEqual, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code class="java">/**
 * 某个时间及以后
 */
private void largeEqual() throws ParseException {
    String createdAt = &quot;2018-11-23 10:30:00&quot;;
    SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);
    Date createdAtDate = sdf.parse(createdAt);
    BmobDate bmobCreatedAtDate = new BmobDate(createdAtDate);


    BmobQuery&lt;Category&gt; categoryBmobQuery = new BmobQuery&lt;&gt;();
    categoryBmobQuery.addWhereGreaterThanOrEqualTo(&quot;createdAt&quot;, bmobCreatedAtDate);
    categoryBmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnEqual, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnEqual, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code class="java">/**
 * 期间
 */
private void duration() throws ParseException {
    SimpleDateFormat sdf = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm:ss&quot;);

    String createdAtStart = &quot;2018-11-23 10:29:59&quot;;
    Date createdAtDateStart = sdf.parse(createdAtStart);
    BmobDate bmobCreatedAtDateStart = new BmobDate(createdAtDateStart);

    String createdAtEnd = &quot;2018-11-23 10:30:01&quot;;
    Date createdAtDateEnd = sdf.parse(createdAtEnd);
    BmobDate bmobCreatedAtDateEnd = new BmobDate(createdAtDateEnd);


    BmobQuery&lt;Category&gt; categoryBmobQueryStart = new BmobQuery&lt;&gt;();
    categoryBmobQueryStart.addWhereGreaterThanOrEqualTo(&quot;createdAt&quot;, bmobCreatedAtDateStart);
    BmobQuery&lt;Category&gt; categoryBmobQueryEnd = new BmobQuery&lt;&gt;();
    categoryBmobQueryEnd.addWhereLessThanOrEqualTo(&quot;createdAt&quot;, bmobCreatedAtDateEnd);
    List&lt;BmobQuery&lt;Category&gt;&gt; queries = new ArrayList&lt;&gt;();
    queries.add(categoryBmobQueryStart);
    queries.add(categoryBmobQueryStart);


    BmobQuery&lt;Category&gt; categoryBmobQuery = new BmobQuery&lt;&gt;();
    categoryBmobQuery.and(queries);
    categoryBmobQuery.findObjects(new FindListener&lt;Category&gt;() {
        @Override
        public void done(List&lt;Category&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnEqual, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnEqual, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h4 id="_20">数组查询<a class="headerlink" href="#_20" title="Permanent link">&para;</a></h4>
<p>对于字段类型为数组的情况，需要查找字段中的数组值是否有被包含的对象，可以使用<code>addWhereContainsAll</code>方法：</p>
<p>查询有A和B别名的用户：</p>
<pre><code class="java">/**
 * 包含所有
 */
private void containAll() {
    BmobQuery&lt;User&gt; userBmobQuery = new BmobQuery&lt;&gt;();
    String[] alias = new String[]{&quot;A&quot;, &quot;B&quot;};
    userBmobQuery.addWhereContainsAll(&quot;alias&quot;, Arrays.asList(alias));
    userBmobQuery.findObjects(new FindListener&lt;User&gt;() {
        @Override
        public void done(List&lt;User&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnContain, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnContain, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h4 id="_21">模糊查询<a class="headerlink" href="#_21" title="Permanent link">&para;</a></h4>
<p>对字符串值的模糊查询 比如 查询包含字符串的值，有几种方法。</p>
<p>你可以使用任何正确的正则表达式来检索相匹配的值，使用<code>addWhereMatches</code>方法：</p>
<pre><code class="java">query.addWhereMatches((&quot;username&quot;, &quot;^[A-Z]\\d&quot;);
</code></pre>

<p>还可以使用如下方法：</p>
<pre><code class="java">//查询username字段的值含有“sm”的数据
query.addWhereContains(&quot;username&quot;, &quot;sm&quot;);

//查询username字段的值是以“sm“字开头的数据
query.whereStartsWith(&quot;username&quot;, &quot;sm&quot;);

// 查询username字段的值是以“ile“字结尾的数据
query.whereEndsWith(&quot;username&quot;, &quot;ile&quot;);
</code></pre>

<p><strong>注:模糊查询只对付费用户开放，付费后可直接使用。</strong></p>
<h4 id="_22">列值是否存在<a class="headerlink" href="#_22" title="Permanent link">&para;</a></h4>
<p>如果你想查询某个列的值存在，那么可以使用<code>addWhereExists</code>方法：</p>
<pre><code class="java">//查询username有值的数据
query.addWhereExists(&quot;username&quot;);
</code></pre>

<p>如果想查询某个列的值不存在，则可以用<code>addWhereDoesNotExists</code>方法</p>
<pre><code class="java">//查询username字段没有值的数据
query.addWhereDoesNotExists(&quot;username&quot;);
</code></pre>

<h4 id="_23">分页查询<a class="headerlink" href="#_23" title="Permanent link">&para;</a></h4>
<p>有时，在数据比较多的情况下，你希望查询出的符合要求的所有数据能按照多少条为一页来显示，这时可以使用<code>setLimit</code>方法来限制查询结果的数据条数来进行分页。</p>
<p>默认情况下，Limit的值为<code>100</code>，最大有效设置值<code>500</code>（设置的数值超过500还是视为500）。</p>
<pre><code class="java">query.setLimit(10); // 限制最多10条数据结果作为一页
</code></pre>

<p>在数据较多的情况下，在<code>setLimit</code>的基础上分页显示数据是比较合理的解决办法。
<code>setSKip</code>方法可以做到跳过查询的前多少条数据来实现分页查询的功能。默认情况下Skip的值为10。</p>
<pre><code class="java">query.setSkip(10); // 忽略前10条数据（即第一页数据结果）
</code></pre>

<p><strong>大家也可以直接下载我们提供的Demo源码（<a href="https://github.com/bmob/bmob-android-demo-paging">https://github.com/bmob/bmob-android-demo-paging</a>），查看如何使用分页查询，结合ListView开发下拉刷新查看更多内容的应用。</strong></p>
<h4 id="_24">排序<a class="headerlink" href="#_24" title="Permanent link">&para;</a></h4>
<p>对应数据的排序，如数字或字符串，你可以使用升序或降序的方式来控制查询数据的结果顺序：</p>
<pre><code class="java">// 根据score字段升序显示数据
query.order(&quot;score&quot;);
// 根据score字段降序显示数据
query.order(&quot;-score&quot;);
// 多个排序字段可以用（，）号分隔
query.order(&quot;-score,createdAt&quot;);
</code></pre>

<p><strong>说明：多个字段排序时，先按第一个字段进行排序，再按第二个字段进行排序，依次进行。</strong></p>
<h3 id="_25">复合查询<a class="headerlink" href="#_25" title="Permanent link">&para;</a></h3>
<h4 id="and">与查询(and)<a class="headerlink" href="#and" title="Permanent link">&para;</a></h4>
<p>有些查询需要使用到复合“与”的查询条件，例如：你想查询出Person表中年龄在6-29岁之间且姓名以"y"或者"e"结尾的人，那么，可以采用and查询，示例代码如下：</p>
<pre><code class="java">//查询年龄6-29岁之间的人，每一个查询条件都需要New一个BmobQuery对象
//--and条件1
BmobQuery&lt;Person&gt; eq1 = new BmobQuery&lt;Person&gt;();
eq1.addWhereLessThanOrEqualTo(&quot;age&quot;, 29);//年龄&lt;=29
//--and条件2
BmobQuery&lt;Person&gt; eq2 = new BmobQuery&lt;Person&gt;();
eq2.addWhereGreaterThanOrEqualTo(&quot;age&quot;, 6);//年龄&gt;=6

//查询姓名以&quot;y&quot;或者&quot;e&quot;结尾的人--这个需要使用到复合或查询（or）
//--and条件3
BmobQuery&lt;Person&gt; eq3 = new BmobQuery&lt;Person&gt;();
eq3.addWhereEndsWith(&quot;name&quot;, &quot;y&quot;);
BmobQuery&lt;Person&gt; eq4 = new BmobQuery&lt;Person&gt;();
eq4.addWhereEndsWith(&quot;name&quot;, &quot;e&quot;);
List&lt;BmobQuery&lt;Person&gt;&gt; queries = new ArrayList&lt;BmobQuery&lt;Person&gt;&gt;();
queries.add(eq3);
queries.add(eq4);
BmobQuery&lt;Person&gt; mainQuery = new BmobQuery&lt;Person&gt;();
BmobQuery&lt;Person&gt; or = mainQuery.or(queries);

//最后组装完整的and条件
List&lt;BmobQuery&lt;Person&gt;&gt; andQuerys = new ArrayList&lt;BmobQuery&lt;Person&gt;&gt;();
andQuerys.add(eq1);
andQuerys.add(eq2);
andQuerys.add(or);
//查询符合整个and条件的人
BmobQuery&lt;Person&gt; query = new BmobQuery&lt;Person&gt;();
query.and(andQuerys);
query.findObjects(new FindListener&lt;Person&gt;() {
    @Override
    public void done(List&lt;Person&gt; object, BmobException e) {
        if(e==null){
            toast(&quot;查询年龄6-29岁之间，姓名以'y'或者'e'结尾的人个数：&quot;+object.size());
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage()+&quot;,&quot;+e.getErrorCode());
        }
    }
});
</code></pre>

<h4 id="or">或查询(or)<a class="headerlink" href="#or" title="Permanent link">&para;</a></h4>
<p>有些情况，在查询的时候需要使用到复合的“或”的查询条件。例如，你想查出 Person 表中 age 等于 29 或者 age 等于 6 的人，可以这样：</p>
<pre><code class="java">BmobQuery&lt;Person&gt; eq1 = new BmobQuery&lt;Person&gt;();
eq1.addWhereEqualTo(&quot;age&quot;, 29);
BmobQuery&lt;Person&gt; eq2 = new BmobQuery&lt;Person&gt;();
eq2.addWhereEqualTo(&quot;age&quot;, 6);
List&lt;BmobQuery&lt;Person&gt;&gt; queries = new ArrayList&lt;BmobQuery&lt;Person&gt;&gt;();
queries.add(eq1);
queries.add(eq2);
BmobQuery&lt;Person&gt; mainQuery = new BmobQuery&lt;Person&gt;();
mainQuery.or(queries);
mainQuery.findObjects(new FindListener&lt;Person&gt;() {
    @Override
    public void done(List&lt;Person&gt; object, BmobException e) {
        if(e==null){
            toast(&quot;查询年龄6-29岁之间，姓名以'y'或者'e'结尾的人个数：&quot;+object.size());
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage()+&quot;,&quot;+e.getErrorCode());
        }
    }
});
</code></pre>

<p>你还可以在此基础上添加更多的约束条件到新创建的 BmobQuery 对象上，表示一个 and 查询操作。</p>
<h3 id="_26">查询结果计数<a class="headerlink" href="#_26" title="Permanent link">&para;</a></h3>
<p>如果你只是想统计满足查询对象的数量，你并不需要获取所有匹配对象的具体数据信息，可以直接使用<code>count</code>替代<code>findObjects</code>。例如，查询一个特定玩家玩的游戏场数：</p>
<pre><code class="java">BmobQuery&lt;GameSauce&gt; query = new BmobQuery&lt;GameSauce&gt;();
query.addWhereEqualTo(&quot;playerName&quot;, &quot;Barbie&quot;);
query.count(GameSauce.class, new CountListener() {
    @Override
    public void done(Integer count, BmobException e) {
        if(e==null){
            toast(&quot;count对象个数为：&quot;+count);
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage()+&quot;,&quot;+e.getErrorCode());
        }
    }
});
</code></pre>

<h3 id="_27">查询指定列<a class="headerlink" href="#_27" title="Permanent link">&para;</a></h3>
<p>有的时候，一张表的数据列比较多，而我们只想查询返回某些列的数据时，我们可以使用BmobQuery对象提供的<code>addQueryKeys</code>方法来实现。如下所示：</p>
<pre><code class="java">//只返回Person表的objectId这列的值
BmobQuery&lt;Person&gt; bmobQuery = new BmobQuery&lt;Person&gt;();
bmobQuery.addQueryKeys(&quot;objectId&quot;);
bmobQuery.findObjects(new FindListener&lt;Person&gt;() {
    @Override
    public void done(List&lt;Person&gt; object, BmobException e) {
        if(e==null){
            toast(&quot;查询成功：共&quot; + object.size() + &quot;条数据。&quot;);
            //注意：这里的Person对象中只有指定列的数据。
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage()+&quot;,&quot;+e.getErrorCode());
        }
    }
});
</code></pre>

<p>指定多列时用<code>,</code>号分隔每列，如：<code>addQueryKeys("objectId,name,age")</code>;</p>
<h3 id="_28">统计查询<a class="headerlink" href="#_28" title="Permanent link">&para;</a></h3>
<p>Bmob为开发者提供了以下关键字或其组合的统计查询操作，分别用于计算<code>总和、平均值、最大值、最小值</code>，同时支持分组和过滤条件。</p>
<table>
<thead>
<tr>
<th align="left">方法名</th>
<th align="left">参数说明</th>
<th align="left">方法说明</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">sum</td>
<td align="left">String[] sumKeys（多个列名）</td>
<td align="left">求某列或多列的和</td>
</tr>
<tr>
<td align="left">average</td>
<td align="left">String[] aveKeys（多个列名）</td>
<td align="left">求某列或多列的平均值</td>
</tr>
<tr>
<td align="left">max</td>
<td align="left">String[] maxKeys（多个列名）</td>
<td align="left">求某列或多列的最大值</td>
</tr>
<tr>
<td align="left">min</td>
<td align="left">String[] minKeys（多个列名）</td>
<td align="left">求某列或多列的最小值</td>
</tr>
<tr>
<td align="left">groupby</td>
<td align="left">String[] groupKeys（多个列名）</td>
<td align="left">分组</td>
</tr>
<tr>
<td align="left">having</td>
<td align="left">HashMap map(键（String）值(Object)对的形式)</td>
<td align="left">分组的过滤条件</td>
</tr>
<tr>
<td align="left">setHasGroupCount</td>
<td align="left">boolean hasCount</td>
<td align="left">是否返回每个分组的记录数</td>
</tr>
</tbody>
</table>
<p>注：
1、为避免和用户创建的列名称冲突，Bmob约定以上查询返回的字段采用<code>_(关键字)+首字母大写的列名</code> 的格式：
例：
计算用户表（_User）中列名为score的总和，那么返回的结果集会有一个列名为<code>_sumScore</code>，
若设置了setHasGroupCount（true）,则结果集中会返回<code>_count</code>。
2、因为返回格式不固定，故使用<code>findStatistics</code>来专门处理统计查询。</p>
<pre><code class="java">/**
 * TODO 不带groupby的查询结果，统计全部。
 * [{
 *  &quot;_avgFault&quot;: 1.625,
 *  &quot;_avgFoul&quot;: 3.75,
 *  &quot;_avgScore&quot;: 25.75,
 *  &quot;_avgSteal&quot;: 2,
 *  &quot;_count&quot;: 79,
 *  &quot;_maxFault&quot;: 3,
 *  &quot;_maxFoul&quot;: 6,
 *  &quot;_maxScore&quot;: 53,
 *  &quot;_maxSteal&quot;: 4,
 *  &quot;_minFault&quot;: 1,
 *  &quot;_minFoul&quot;: 2,
 *  &quot;_minScore&quot;: 11,
 *  &quot;_minSteal&quot;: 1,
 *  &quot;_sumFault&quot;: 13,
 *  &quot;_sumFoul&quot;: 30,
 *  &quot;_sumScore&quot;: 206,
 *  &quot;_sumSteal&quot;: 16
 * }]
 */
/**
 * TODO 带groupby的查询结果，根据country分组统计。
 * [{
 * &quot;_avgFault&quot;: 1.6666666666666667,
 * &quot;_avgFoul&quot;: 2.3333333333333335,
 * &quot;_avgScore&quot;: 25.666666666666668,
 * &quot;_avgSteal&quot;: 1.3333333333333333,
 * &quot;_count&quot;: 3,
 * &quot;_maxFault&quot;: 2,
 * &quot;_maxFoul&quot;: 3,
 * &quot;_maxScore&quot;: 53,
 * &quot;_maxSteal&quot;: 2,
 * &quot;_minFault&quot;: 1,
 * &quot;_minFoul&quot;: 2,
 * &quot;_minScore&quot;: 12,
 * &quot;_minSteal&quot;: 1,
 * &quot;_sumFault&quot;: 5,
 * &quot;_sumFoul&quot;: 7,
 * &quot;_sumScore&quot;: 77,
 * &quot;_sumSteal&quot;: 4,
 * &quot;country&quot;: &quot;china&quot;
 * }, {
 * &quot;_avgFault&quot;: 2,
 * &quot;_avgFoul&quot;: 4.5,
 * &quot;_avgScore&quot;: 22,
 * &quot;_avgSteal&quot;: 2.5,
 * &quot;_count&quot;: 2,
 * &quot;_maxFault&quot;: 3,
 * &quot;_maxFoul&quot;: 5,
 * &quot;_maxScore&quot;: 23,
 * &quot;_maxSteal&quot;: 3,
 * &quot;_minFault&quot;: 1,
 * &quot;_minFoul&quot;: 4,
 * &quot;_minScore&quot;: 21,
 * &quot;_minSteal&quot;: 2,
 * &quot;_sumFault&quot;: 4,
 * &quot;_sumFoul&quot;: 9,
 * &quot;_sumScore&quot;: 44,
 * &quot;_sumSteal&quot;: 5,
 * &quot;country&quot;: &quot;usa&quot;
 * }, {
 * &quot;_avgFault&quot;: 1.3333333333333333,
 * &quot;_avgFoul&quot;: 4.666666666666667,
 * &quot;_avgScore&quot;: 28.333333333333332,
 * &quot;_avgSteal&quot;: 2.3333333333333335,
 * &quot;_count&quot;: 3,
 * &quot;_maxFault&quot;: 2,
 * &quot;_maxFoul&quot;: 6,
 * &quot;_maxScore&quot;: 43,
 * &quot;_maxSteal&quot;: 4,
 * &quot;_minFault&quot;: 1,
 * &quot;_minFoul&quot;: 2,
 * &quot;_minScore&quot;: 11,
 * &quot;_minSteal&quot;: 1,
 * &quot;_sumFault&quot;: 4,
 * &quot;_sumFoul&quot;: 14,
 * &quot;_sumScore&quot;: 85,
 * &quot;_sumSteal&quot;: 7,
 * &quot;country&quot;: &quot;uk&quot;
 * }, {
 * &quot;_avgFault&quot;: null,
 * &quot;_avgFoul&quot;: null,
 * &quot;_avgScore&quot;: null,
 * &quot;_avgSteal&quot;: null,
 * &quot;_count&quot;: 71,
 * &quot;_maxFault&quot;: null,
 * &quot;_maxFoul&quot;: null,
 * &quot;_maxScore&quot;: null,
 * &quot;_maxSteal&quot;: null,
 * &quot;_minFault&quot;: null,
 * &quot;_minFoul&quot;: null,
 * &quot;_minScore&quot;: null,
 * &quot;_minSteal&quot;: null,
 * &quot;_sumFault&quot;: 0,
 * &quot;_sumFoul&quot;: 0,
 * &quot;_sumScore&quot;: 0,
 * &quot;_sumSteal&quot;: 0,
 * &quot;country&quot;: null
 * }]
 */

/**
 * TODO 带groupby和having的查询结果
 * [{
 *  &quot;_avgFault&quot;: 1.3333333333333333,
 *  &quot;_avgFoul&quot;: 4.666666666666667,
 *  &quot;_avgScore&quot;: 28.333333333333332,
 *  &quot;_avgSteal&quot;: 2.3333333333333335,
 *  &quot;_count&quot;: 3,
 *  &quot;_maxFault&quot;: 2,
 *  &quot;_maxFoul&quot;: 6,
 *  &quot;_maxScore&quot;: 43,
 *  &quot;_maxSteal&quot;: 4,
 *  &quot;_minFault&quot;: 1,
 *  &quot;_minFoul&quot;: 2,
 *  &quot;_minScore&quot;: 11,
 *  &quot;_minSteal&quot;: 1,
 *  &quot;_sumFault&quot;: 4,
 *  &quot;_sumFoul&quot;: 14,
 *  &quot;_sumScore&quot;: 85,
 *  &quot;_sumSteal&quot;: 7,
 *  &quot;country&quot;: &quot;uk&quot;
 * }]
 */

/**
 * “group by”从字面意义上理解就是根据“by”指定的规则对数据进行分组，所谓的分组就是将一个“数据集”划分成若干个“小区域”，然后针对若干个“小区域”进行数据处理。
 * where 子句的作用是在对查询结果进行分组前，将不符合where条件的行去掉，即在分组之前过滤数据，where条件中不能包含聚组函数，使用where条件过滤出特定的行。
 * having 子句的作用是筛选满足条件的组，即在分组之后过滤数据，条件中经常包含聚组函数，使用having 条件过滤出特定的组，也可以使用多个分组标准进行分组。
 *
 * @throws JSONException
 */
private void statistics() throws JSONException {
    BmobQuery&lt;User&gt; bmobQuery = new BmobQuery&lt;&gt;();
    //总和
    bmobQuery.sum(new String[]{&quot;score&quot;, &quot;steal&quot;, &quot;foul&quot;, &quot;fault&quot;});
    //平均值
    bmobQuery.average(new String[]{&quot;score&quot;, &quot;steal&quot;, &quot;foul&quot;, &quot;fault&quot;});
    //最大值
    bmobQuery.max(new String[]{&quot;score&quot;, &quot;steal&quot;, &quot;foul&quot;, &quot;fault&quot;});
    //最小值
    bmobQuery.min(new String[]{&quot;score&quot;, &quot;steal&quot;, &quot;foul&quot;, &quot;fault&quot;});
    //是否返回所统计的总条数
    bmobQuery.setHasGroupCount(true);
    //根据所给列分组统计
    bmobQuery.groupby(new String[]{&quot;country&quot;});
    //对统计结果进行过滤
    HashMap&lt;String, Object&gt; map = new HashMap&lt;&gt;(1);
    JSONObject jsonObject = new JSONObject();
    jsonObject.put(&quot;$gt&quot;, 28);
    map.put(&quot;_avgScore&quot;, jsonObject);
    bmobQuery.having(map);
    //开始统计查询
    bmobQuery.findStatistics(User.class, new QueryListener&lt;JSONArray&gt;() {
        @Override
        public void done(JSONArray jsonArray, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnStatistics, &quot;查询成功：&quot; + jsonArray.length(), Snackbar.LENGTH_LONG).show();
                try {
                    JSONObject jsonObject = jsonArray.getJSONObject(0);
                    int sum = jsonObject.getInt(&quot;_sumScore&quot;);
                    Snackbar.make(mBtnStatistics, &quot;sum：&quot; + sum, Snackbar.LENGTH_LONG).show();
                } catch (JSONException e1) {
                    e1.printStackTrace();
                }
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnStatistics, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}

</code></pre>

<h3 id="_29">缓存查询<a class="headerlink" href="#_29" title="Permanent link">&para;</a></h3>
<p>缓存查询通常是将查询结果缓存在磁盘上。
当用户的设备处于离线状态时，就可以从缓存中获取数据来显示。
或者在应用界面刚刚启动，从网络获取数据还未得到结果时，先使用缓存数据来显示。
这样可以让用户不必在按下某个按钮后进行枯燥的等待。
默认的查询操作是没有启用缓存的，开发者可以使用<code>setCachePolicy</code>方法来启用缓存功能。
例如：优先从缓存获取数据，如果获取失败再从网络获取数据。</p>
<pre><code class="java">bmobQuery.setCachePolicy(CachePolicy.CACHE_ELSE_NETWORK);   // 先从缓存获取数据，如果没有，再从网络获取。
bmobQuery.findObjects(new FindListener&lt;Person&gt;() {
    @Override
    public void done(List&lt;Person&gt; object,BmobException e) {
        if(e==null){
            toast(&quot;查询成功：共&quot;+object.size()+&quot;条数据。&quot;);
        }else{
            toast(&quot;查询失败：&quot;+msg);
        }
    }

});
</code></pre>

<h4 id="_30">缓存策略<a class="headerlink" href="#_30" title="Permanent link">&para;</a></h4>
<p>Bmob SDK提供了几种不同的缓存策略，以适应不同应用场景的需求：</p>
<ul>
<li><code>IGNORE_CACHE</code>     :只从网络获取数据，且不会将数据缓存在本地，这是默认的缓存策略。</li>
<li><code>CACHE_ONLY</code>        :只从缓存读取数据，如果缓存没有数据会导致一个BmobException,可以忽略不处理这个BmobException.</li>
<li><code>NETWORK_ONLY</code>      :只从网络获取数据，同时会在本地缓存数据。</li>
<li><code>NETWORK_ELSE_CACHE</code>:先从网络读取数据，如果没有，再从缓存中获取。</li>
<li><code>CACHE_ELSE_NETWORK</code>:先从缓存读取数据，如果没有，再从网络获取。</li>
<li><code>CACHE_THEN_NETWORK</code>:先从缓存取数据，无论结果如何都会再次从网络获取数据。也就是说会产生2次调用。</li>
</ul>
<p><strong>建议的做法：</strong>
<strong>第一次进入应用的时候，设置其查询的缓存策略为<code>CACHE_ELSE_NETWORK</code>,当用户执行上拉或者下拉刷新操作时，设置查询的缓存策略为<code>NETWORK_ELSE_CACHE</code>。</strong></p>
<h4 id="_31">缓存方法<a class="headerlink" href="#_31" title="Permanent link">&para;</a></h4>
<p>如果需要操作缓存内容，可以使用BmobQuery提供的方法做如下操作：</p>
<ul>
<li>检查是否存在当前查询条件的缓存数据</li>
</ul>
<pre><code class="java">boolean isInCache = query.hasCachedResult(Class&lt;?&gt; clazz);
</code></pre>

<p><strong>注：缓存和查询条件有关，此方法必须放在所有的查询条件（where、limit、order、skip、include等）都设置完之后，否则会得不到缓存数据。</strong></p>
<ul>
<li>清除当前查询的缓存数据</li>
</ul>
<pre><code class="java">query.clearCachedResult(Class&lt;?&gt; clazz);
</code></pre>

<ul>
<li>清除所有查询结果的缓存数据</li>
</ul>
<pre><code class="java">BmobQuery.clearAllCachedResults(Class&lt;?&gt; clazz);
</code></pre>

<ul>
<li>设置缓存的最长时间（以毫秒为单位）</li>
</ul>
<pre><code class="java">query.setMaxCacheAge(TimeUnit.DAYS.toMillis(1));//此表示缓存一天
</code></pre>

<p>示例如下：</p>
<pre><code class="java">BmobQuery&lt;Person&gt; query  = new BmobQuery&lt;Person&gt;();
query.addWhereEqualTo(&quot;age&quot;, 25);
query.setLimit(10);
query.order(&quot;createdAt&quot;);
//判断是否有缓存，该方法必须放在查询条件（如果有的话）都设置完之后再来调用才有效，就像这里一样。
boolean isCache = query.hasCachedResult(Person.class);
if(isCache){--此为举个例子，并不一定按这种方式来设置缓存策略
    query.setCachePolicy(CachePolicy.CACHE_ELSE_NETWORK);   // 如果有缓存的话，则设置策略为CACHE_ELSE_NETWORK
}else{
    query.setCachePolicy(CachePolicy.NETWORK_ELSE_CACHE);   // 如果没有缓存的话，则设置策略为NETWORK_ELSE_CACHE
}
query.findObjects(new FindListener&lt;Person&gt;() {

    @Override
    public void done(List&lt;Person&gt; object,BmobException e) {
        if(e==null){
            toast(&quot;查询成功：共&quot;+object.size()+&quot;条数据。&quot;);
        }else{
            toast(&quot;查询失败：&quot;+msg);
        }
    }
});
</code></pre>

<p><strong>注：</strong>
<strong>1、只有当缓存查询的条件一模一样时才会获取到缓存到本地的缓存数据。</strong>
<strong>2、设置的默认的最大缓存时长为5小时。</strong></p>
<h3 id="bql">BQL查询<a class="headerlink" href="#bql" title="Permanent link">&para;</a></h3>
<p><code>Bmob Query Language</code>（简称 BQL） 是 Bmob 自 <code>BmobSDKV3.3.7</code> 版本开始，为查询 API 定制的一套类似 SQL 查询语法的子集和变种，主要目的是降低大家学习 Bmob 查询API 的成本，可以使用传统的 SQL 语法来查询 Bmob 应用内的数据。</p>
<p>具体的 BQL 语法，请参考 <a href="http://doc.bmob.cn/other/bql/">Bmob Query Language 详细指南</a>。</p>
<h4 id="bql_1">基本BQL查询<a class="headerlink" href="#bql_1" title="Permanent link">&para;</a></h4>
<p>可以通过以下方法来进行SQL查询：
例如：需要查询所有的游戏得分记录</p>
<pre><code class="java">String bql =&quot;select * from GameScore&quot;;//查询所有的游戏得分记录
new BmobQuery&lt;GameScore&gt;().doSQLQuery(bql,new SQLQueryListener&lt;GameScore&gt;(){

    @Override
    public void done(BmobQueryResult&lt;GameScore&gt; result, BmobException e) {
        if(e ==null){
            List&lt;GameScore&gt; list = (List&lt;GameScore&gt;) result.getResults();
            if(list!=null &amp;&amp; list.size()&gt;0){
                ...
            }else{
                Log.i(&quot;smile&quot;, &quot;查询成功，无数据返回&quot;);
            }
        }else{
            Log.i(&quot;smile&quot;, &quot;错误码：&quot;+e.getErrorCode()+&quot;，错误描述：&quot;+e.getMessage());
        }
    }
});
</code></pre>

<p>上面的示例也等价于(<code>此方法自BmobV3.3.8版本提供</code>)：</p>
<pre><code class="java">//查询所有的游戏得分记录
String bql =&quot;select * from GameScore&quot;;
BmobQuery&lt;GameScore&gt; query=new BmobQuery&lt;GameScore&gt;();
//设置查询的SQL语句
query.setSQL(bql);
query.doSQLQuery(new SQLQueryListener&lt;GameScore&gt;(){

    @Override
    public void done(BmobQueryResult&lt;GameScore&gt; result, BmobException e) {
        if(e ==null){
            List&lt;GameScore&gt; list = (List&lt;GameScore&gt;) result.getResults();
            if(list!=null &amp;&amp; list.size()&gt;0){
                ...
            }else{
                Log.i(&quot;smile&quot;, &quot;查询成功，无数据返回&quot;);
            }
        }else{
            Log.i(&quot;smile&quot;, &quot;错误码：&quot;+e.getErrorCode()+&quot;，错误描述：&quot;+e.getMessage());
        }
    }
});
</code></pre>

<p>如果需要查询个数，则可以这样：</p>
<pre><code class="java">String bql = &quot;select count(*),* from GameScore&quot;;//查询GameScore表中总记录数并返回所有记录信息
new BmobQuery&lt;GameScore&gt;().doSQLQuery(bql, new SQLQueryListener&lt;GameScore&gt;(){

    @Override
    public void done(BmobQueryResult&lt;GameScore&gt; result, BmobException e) {
        if(e ==null){
            int count = result.getCount();//这里得到符合条件的记录数
            List&lt;GameScore&gt; list = (List&lt;GameScore&gt;) result.getResults();
            if(list.size()&gt;0){
                ...
            }else{
                Log.i(&quot;smile&quot;, &quot;查询成功，无数据&quot;);
            }
        }else{
            Log.i(&quot;smile&quot;, &quot;错误码：&quot;+e.getErrorCode()+&quot;，错误描述：&quot;+e.getMessage());
        }
    }
});
</code></pre>

<p>注：当查询的表为系统表（目前系统表有User、Installation、Role）时，需要带上下划线 <code>_</code>。</p>
<p>比如，你想查询的是用户<code>smile</code>的信息，则：</p>
<pre><code class="sql">select * from _User where username = smile
</code></pre>

<h4 id="bql_2">统计BQL查询<a class="headerlink" href="#bql_2" title="Permanent link">&para;</a></h4>
<p>由于统计查询的结果是不定的，故BQL提供了另外一种查询方法来进行统计查询，可以使用<code>doStatisticQuery</code>方法来进行：</p>
<pre><code class="java">//按照姓名分组求和,并将结果按时间降序排列
String bql = &quot;select sum(playScore) from GameScore group by name order by -createdAt&quot;;
new BmobQuery&lt;GameScore&gt;().doStatisticQuery(bql,new QueryListener&lt;JSONArray&gt;(){

    @Override
    public void done(Object result, BmobException e) {
        if(e ==null){
            JSONArray ary = (JSONArray) result;
            if(ary!=null){//开发者需要根据返回结果自行解析数据
                ...
            }else{
                showToast(&quot;查询成功，无数据&quot;);
            }
        }else{
            Log.i(&quot;smile&quot;, &quot;错误码：&quot;+e.getErrorCode()+&quot;，错误描述：&quot;+e.getMessage());
        }
    }
});
</code></pre>

<h4 id="_32">占位符查询<a class="headerlink" href="#_32" title="Permanent link">&para;</a></h4>
<p>在更多的时候，一个查询语句中间会有很多的值是可变值，为此，我们也提供了类似 Java JDBC 里的 <code>PreparedStatement</code> 使用占位符查询的语法结构。</p>
<h5 id="_33">普通查询<a class="headerlink" href="#_33" title="Permanent link">&para;</a></h5>
<pre><code class="java">String bql=&quot;select * from GameScore where player = ? and game = ?&quot;;//查询玩家1的地铁跑酷的GameScore信息
new BmobQuery&lt;GameScore&gt;().doSQLQuery(bql,new SQLQueryListener&lt;GameScore&gt;(){

    @Override
    public void done(BmobQueryResult&lt;GameScore&gt; result, BmobException e) {
        if(e ==null){
            List&lt;GameScore&gt; list = (List&lt;GameScore&gt;) result.getResults();
            if(list!=null &amp;&amp; list.size()&gt;0){
                ...
            }else{
                Log.i(&quot;smile&quot;, &quot;查询成功，无数据返回&quot;);
            }
        }else{
            Log.i(&quot;smile&quot;, &quot;错误码：&quot;+e.getErrorCode()+&quot;，错误描述：&quot;+e.getMessage());
        }
    }
},&quot;玩家1&quot;,&quot;地铁跑酷&quot;);
</code></pre>

<p>最后的可变参数 <code>玩家1</code> 和 <code>地铁跑酷</code> 会自动替换查询语句中的问号位置（按照问号的先后出现顺序）。</p>
<p>上面的示例也等价于如下代码（<code>此方法自BmobV3.3.8版本提供</code>）：</p>
<pre><code class="java">String bql=&quot;select * from GameScore where player = ? and game = ?&quot;;
BmobQuery&lt;GameScore&gt; query=new BmobQuery&lt;GameScore&gt;();
//设置SQL语句
query.setSQL(bql);
//设置占位符参数
query.setPreparedParams(new Object[]{&quot;玩家1&quot;,&quot;地铁跑酷&quot;});
query.doSQLQuery(new SQLQueryListener&lt;GameScore&gt;(){

    @Override
    public void done(BmobQueryResult&lt;GameScore&gt; result, BmobException e) {
        if(e ==null){
            List&lt;GameScore&gt; list = (List&lt;GameScore&gt;) result.getResults();
            if(list!=null &amp;&amp; list.size()&gt;0){
                ...
            }else{
                Log.i(&quot;smile&quot;, &quot;查询成功，无数据返回&quot;);
            }
        }else{
            Log.i(&quot;smile&quot;, &quot;错误码：&quot;+e.getErrorCode()+&quot;，错误描述：&quot;+e.getMessage());
        }
    }
});
</code></pre>

<h5 id="_34">内置函数<a class="headerlink" href="#_34" title="Permanent link">&para;</a></h5>
<p>对于包含<code>内置函数</code>的占位符查询，比较特殊，请使用<a href="http://doc.bmob.cn/other/bql/"><code>Bmob Query Language 详细指南</code></a>中的<code>内置函数</code>中占位符查询用到的内置函数列出的形式进行查询操作：</p>
<p>举例：我想查询当前用户在2015年5月12日之后，在特定地理位置附近的游戏记录，可以这样：</p>
<pre><code class="java">String sql = &quot;select * from GameScore where createdAt &gt; date(?) and player = pointer(?,?) and gps near geopoint(?,?)&quot;;
new BmobQuery&lt;GameScore&gt;().doSQLQuery(sql,new SQLQueryListener&lt;GameScore&gt;(){

    @Override
    public void done(BmobQueryResult&lt;GameScore&gt; result, BmobException e) {
        if(e ==null){
            List&lt;GameScore&gt; list = (List&lt;GameScore&gt;) result.getResults();
            if(list!=null &amp;&amp; list.size()&gt;0){
                ...
            }else{
                Log.i(&quot;smile&quot;, &quot;查询成功，无数据返回&quot;);
            }
        }else{
            Log.i(&quot;smile&quot;, &quot;错误码：&quot;+e.getErrorCode()+&quot;，错误描述：&quot;+e.getMessage());
        }
    }
},&quot;2015-05-12 00:00:00&quot;,&quot;_User&quot;,user.getObjectId(),112.934755,24.52065);
</code></pre>

<p><strong>注</strong>
<strong>1、我们更推荐使用占位符语法，理论上会降低 BQL 转换的性能开销；</strong>
<strong>2、最后的可变参数会自动替换查询语句中的问号位置（按照问号的先后出现顺序），有多少个问号，最后的可变参数就应该有多少个；</strong>
<strong>3、同样的，统计查询也支持占位符,只需要将<code>doSQLQuery</code>替换成<code>doStatisticQuery</code>方法即可；</strong>
<strong>4、只有查询条件<code>where``limit</code>子句支持占位符查询，和统计查询有关的<code>group by</code>、<code>order by</code>、<code>having</code>等字句是不支持占位符的。</strong></p>
<p>例如：
<code>正确</code>查询：</p>
<pre><code class="java">//按照游戏名进行分组并获取总得分数大于200的统计信息，同时统计各分组的记录数
String bql = &quot;select sum(playScore),count(*) from GameScore group by game having _sumPlayScore&gt;200&quot;;
new BmobQuery&lt;GameScore&gt;().doStatisticQuery(bql,new StatisticQueryListener(){

    @Override
    public void done(Object result, BmobException e) {
        ...
    }
});
</code></pre>

<p><code>错误</code>查询：</p>
<pre><code class="java">String bql = &quot;select sum(playScore),count(*) from GameScore group by ? having ?&quot;;
new BmobQuery&lt;GameScore&gt;().doStatisticQuery(bql,new StatisticQueryListener(){

    @Override
    public void done(Object result, BmobException e) {
        ...
    }
},&quot;game&quot;,&quot;_sumPlayScore&gt;200&quot;);
</code></pre>

<h4 id="bql_3">BQL缓存查询<a class="headerlink" href="#bql_3" title="Permanent link">&para;</a></h4>
<p>BQL查询同步支持<code>缓存查询</code>，只需要调用BmobQuery的<code>setCachePolicy</code>方法设置缓存策略即可，<code>建议使用如下方式进行BQL缓存查询</code>：</p>
<pre><code class="java">String sql = &quot;select * from GameScore order by playScore,signScore desc&quot;;
BmobQuery&lt;GameScore&gt; query = new BmobQuery&lt;GameScore&gt;();
//设置sql语句
query.setSQL(sql);
//判断此查询本地是否存在缓存数据
boolean isCache = query.hasCachedResult(GameScore.class);
if(isCache){
    query.setCachePolicy(CachePolicy.CACHE_ELSE_NETWORK);   // 如果有缓存的话，则设置策略为CACHE_ELSE_NETWORK
}else{
    query.setCachePolicy(CachePolicy.NETWORK_ELSE_CACHE);   // 如果没有缓存的话，则设置策略为NETWORK_ELSE_CACHE
}
query.doSQLQuery(new SQLQueryListener&lt;GameScore&gt;(){

    @Override
    public void done(BmobQueryResult&lt;GameScore&gt; result, BmobException e) {
        if(e ==null){
            Log.i(&quot;smile&quot;, &quot;查询到：&quot;+result.getResults().size()+&quot;符合条件的数据&quot;);
        }else{
            Log.i(&quot;smile&quot;, &quot;错误码：&quot;+e.getErrorCode()+&quot;，错误描述：&quot;+e.getMessage());
        }
    }
});
</code></pre>

<p><strong>注：</strong>
<strong>doSQLQuery目前有三种查询方式进行SQL查询，分别是：</strong>
<strong>1、doSQLQuery（Context context,SQLQueryListener<T> listener)</strong>
<strong>2、doSQLQuery（Context context, String bql, SQLQueryListener<T> listener)----基本BQL查询</strong>
<strong>3、doSQLQuery（Context context, String bql, SQLQueryListener<T> listener,Object... params)----占位符查询</strong>
<strong>只有<code>第一种查询方式</code>才能和<code>query.hasCachedResult(context,class)</code>或者<code>query.clearCachedResult(context,class)</code>并列使用。</strong>
<strong>建议使用<code>第一种查询方式</code>进行BQL缓存查询。</strong></p>
<h3 id="include">include用法<a class="headerlink" href="#include" title="Permanent link">&para;</a></h3>
<p>在某些情况下，你想在一个查询内获取<code>Pointer</code>类型的关联对象。</p>
<p>比如上述示例中，如果希望在查询帖子信息的同时也把该帖子的作者的信息查询出来，可以使用<code>include</code>方法</p>
<pre><code class="java">query.include(&quot;author&quot;);
</code></pre>

<p>你可以使用<code>,</code>号(逗号)操作符来<code>include并列查询</code>两个对象</p>
<p>比如，查询评论表的同时将该评论用户的信息和所评论的帖子信息也一并查询出来（因为Comment表有两个<code>Pointer类型</code>的字段），那么可以这样做：</p>
<pre><code class="java">query.include(&quot;user,post&quot;);
</code></pre>

<p>但不能如下的做法：</p>
<pre><code class="java">query.include(&quot;user&quot;);
query.include(&quot;post&quot;);
</code></pre>

<p>你同时还可以使用 <code>.</code>号（英语句号）操作符来进行<code>include中的内嵌对象查询</code></p>
<p>比如，你想在查询评论信息的同时将该评论<code>Comment</code>对应的帖子<code>post</code>以及该帖子的作者信息<code>author</code>一并查询出来，你可以这样做：</p>
<pre><code class="java">query.include(&quot;post.author&quot;);
</code></pre>

<p>另外，include还可以指定返回的字段：</p>
<pre><code class="java">query.include(&quot;post[likes].author[username|email]&quot;);
</code></pre>

<p>其中，post和author都是Pointer类型，post指向的表只返回likes字段，author指向的表只返回username和email字段。</p>
<p><strong>注：include的查询对象只能为BmobPointer类型，而不能是BmobRelation类型。</strong></p>
<h3 id="_35">内部查询<a class="headerlink" href="#_35" title="Permanent link">&para;</a></h3>
<p>如果你在查询某个对象列表时，它们的某个字段是BmobObject类型，并且这个BmobObject匹配一个不同的查询，这种情况下可使用<code>addWhereMatchesQuery</code>方法。</p>
<p>请注意，默认的 limit 限制 100 也同样作用在内部查询上。因此如果是大规模的数据查询，你可能需要仔细构造你的查询对象来获取想要的行为。</p>
<p>例如：<strong>查询带有图片的帖子的评论列表</strong>:</p>
<pre><code class="java">BmobQuery&lt;Comment&gt; query = new BmobQuery&lt;Comment&gt;();
BmobQuery&lt;Post&gt; innerQuery = new BmobQuery&lt;Post&gt;();
innerQuery.addWhereExists(&quot;image&quot;, true);
// 第一个参数为评论表中的帖子字段名post
// 第二个参数为Post字段的表名，也可以直接用&quot;Post&quot;字符串的形式
// 第三个参数为内部查询条件
query.addWhereMatchesQuery(&quot;post&quot;, &quot;Post&quot;, innerQuery);
query.findObjects(new FindListener&lt;Comment&gt;() {

    @Override
    public void done(List&lt;Comment&gt; object,BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage());
        }
    }
});
</code></pre>

<p>反之，不想匹配某个子查询，你可以使用<code>addWhereDoesNotMatchQuery</code>方法。</p>
<p>比如<strong>查询不带图片的帖子的评论列表</strong>：</p>
<pre><code class="java">BmobQuery&lt;Comment&gt; query = new BmobQuery&lt;Comment&gt;();
BmobQuery&lt;Post&gt; innerQuery = new BmobQuery&lt;Post&gt;();
innerQuery.addWhereExists(&quot;image&quot;, true);
// 第一个参数为评论表中的帖子字段名post
// 第二个参数为Post字段的表名，也可以直接用&quot;Post&quot;字符串的形式
// 第三个参数为内部查询条件
query.addWhereDoesNotMatchQuery(&quot;post&quot;, &quot;Post&quot;, innerQuery);
query.findObjects(new FindListener&lt;Comment&gt;() {
    @Override
    public void done(List&lt;Comment&gt; object,BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage());
        }
    }
});
</code></pre>

<p><strong>注：</strong></p>
<p><code>当查询的表为系统表（目前系统表有User、Installation、Role）时，需要带上下划线</code>_<code>。</code></p>
<p>比如，你想查询出用户<code>smile</code>和<code>smile</code>好友的所有帖子，则可以这样：</p>
<pre><code class="sql">
BmobQuery&lt;User&gt; innerQuery = new BmobQuery&lt;User&gt;();
String[] friendIds={&quot;ssss&quot;,&quot;aaaa&quot;};//好友的objectId数组
innerQuery.addWhereContainedIn(&quot;objectId&quot;, Arrays.asList(friendIds));
//查询帖子
BmobQuery&lt;Post&gt; query = new BmobQuery&lt;Post&gt;();
`query.addWhereMatchesQuery(&quot;author&quot;, &quot;_User&quot;, innerQuery);`
query.findObjects(new FindListener&lt;Post&gt;() {
    @Override
    public void done(List&lt;Post&gt; object,BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage());
        }
    }
});
</code></pre>

<h1 id="5">5、数组操作<a class="headerlink" href="#5" title="Permanent link">&para;</a></h1>
<p>对于数组类型数据，BmobSDK提供了3种操作来原子性地修改一个数组字段的值：</p>
<ul>
<li><strong>add、addAll</strong> 在一个数组字段的后面添加一些指定的对象(包装在一个数组内)</li>
<li><strong>addUnique、addAllUnique</strong> 只会在原本数组字段中没有这些对象的情形下才会添加入数组，插入数组的位置随机</li>
<li><strong>removeAll</strong> 从一个数组字段的值内移除指定数组中的所有对象</li>
</ul>
<p>举例子：</p>
<pre><code class="java">public class Person extends BmobObject {
    private List&lt;String&gt; hobbys;        // 爱好-对应服务端Array类型：String类型的集合
    private List&lt;BankCard&gt; cards;       // 银行卡-对应服务端Array类型:Object类型的集合
    ...
    getter、setter方法
}

其中BankCard类结构如下：
public class BankCard{
    private String cardNumber;
    private String bankName;
    public BankCard(String bankName, String cardNumber){
        this.bankName = bankName;
        this.cardNumber = cardNumber;
    }
    ...
    getter、setter方法
}
</code></pre>

<h3 id="_36">添加数组数据<a class="headerlink" href="#_36" title="Permanent link">&para;</a></h3>
<p>给<code>Person</code>对象中的数组类型字段添加数据,有以下两种方式：</p>
<h4 id="addaddall">使用add、addAll添加<a class="headerlink" href="#addaddall" title="Permanent link">&para;</a></h4>
<pre><code class="java">Person p = new Person();
p.setObjectId(&quot;d32143db92&quot;);
//添加String类型的数组
p.add(&quot;hobbys&quot;, &quot;唱歌&quot;);                              // 添加单个String
//p.addAll(&quot;hobbys&quot;, Arrays.asList(&quot;游泳&quot;, &quot;看书&quot;));    // 添加多个String
//添加Object类型的数组
p.add(&quot;cards&quot;,new BankCard(&quot;工行卡&quot;, &quot;工行卡账号&quot;))   //添加单个Object
List&lt;BankCard&gt; cards =new ArrayList&lt;BankCard&gt;();
for(int i=0;i&lt;2;i++){
    cards.add(new BankCard(&quot;建行卡&quot;+i, &quot;建行卡账号&quot;+i));
}
//p.addAll(&quot;cards&quot;, cards);                         //添加多个Object值
p.update(new UpdateListener() {

    @Override
    public void done(BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;更新成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;更新失败：&quot;+e.getMessage());
        }
    }

});
</code></pre>

<p><strong> 注：此类方法不管这些数据之前是否已添加过，都会再次添加。</strong></p>
<h4 id="adduniqueaddallunique">使用addUnique、addAllUnique添加<a class="headerlink" href="#adduniqueaddallunique" title="Permanent link">&para;</a></h4>
<pre><code class="java">Person p = new Person();
p.setObjectId(&quot;d32143db92&quot;);
//添加String类型的数组
p.addUnique(&quot;hobbys&quot;, &quot;唱歌&quot;);                                // 添加单个String
//p.addAllUnique(&quot;hobbys&quot;, Arrays.asList(&quot;游泳&quot;, &quot;看书&quot;));  // 添加多个String
//添加Object类型的数组
p.addUnique(&quot;cards&quot;,new BankCard(&quot;工行卡&quot;, &quot;工行卡账号&quot;))     //添加单个Object
List&lt;BankCard&gt; cards =new ArrayList&lt;BankCard&gt;();
for(int i=0;i&lt;2;i++){
    cards.add(new BankCard(&quot;建行卡&quot;+i, &quot;建行卡账号&quot;+i));
}
//p.addAllUnique(&quot;cards&quot;, cards);                           //添加多个Object
p.update(new UpdateListener() {

    @Override
    public void done(BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage());
        }
    }

});
</code></pre>

<p><strong> 注： 只有在这些数据之前未添加过的情况下才会被添加。</strong></p>
<h3 id="_37">更新数组数据<a class="headerlink" href="#_37" title="Permanent link">&para;</a></h3>
<p>数组更新比较特殊，自<code>V3.4.4</code>版本开始提供<code>BmobObject</code>的<code>setValue</code>方法来更新数组，例：</p>
<pre><code class="java">Person p2 = new Person();
//更新String类型数组中的值
p2.setValue(&quot;hobbys.0&quot;,&quot;爬山&quot;);                             //将hobbys中第一个位置的爱好（上面添加成功的唱歌）修改为爬山
//更新Object类型数组中的某个位置的对象值(0对应集合中第一个元素)
p2.setValue(&quot;cards.0&quot;, new BankCard(&quot;中行&quot;, &quot;中行卡号&quot;));    //将cards中第一个位置银行卡修改为指定BankCard对象
//更新Object类型数组中指定对象的指定字段的值
//  p2.setValue(&quot;cards.0.bankName&quot;, &quot;农行卡&quot;);             //将cards中第一个位置的银行卡名称修改为农行卡
//  p2.setValue(&quot;cards.1.cardNumber&quot;, &quot;农行卡账号&quot;);         //将cards中第二个位置的银行卡账号修改为农行卡账号
p2.update(objectId, new UpdateListener() {
    @Override
    public void done(BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage());
        }
    }
});
</code></pre>

<h3 id="_38">删除数组数据<a class="headerlink" href="#_38" title="Permanent link">&para;</a></h3>
<p>同理我们也可以使用removeAll从数组字段中移除某些值：</p>
<pre><code class="java">Person p = new Person();
p.removeAll(&quot;hobby&quot;, Arrays.asList(&quot;阅读&quot;,&quot;唱歌&quot;,&quot;游泳&quot;));
p.update(new UpdateListener() {

    @Override
    public void done(BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage());
        }
    }
});
</code></pre>

<h3 id="_39">查询数组数据<a class="headerlink" href="#_39" title="Permanent link">&para;</a></h3>
<p>对于字段类型为数组的情况，可以以数组字段中包含有xxx的数据为条件进行查询：</p>
<pre><code class="java">BmobQuery&lt;Person&gt; query = new BmobQuery&lt;Person&gt;();
String [] hobby = {&quot;阅读&quot;,&quot;唱歌&quot;};
query.addWhereContainsAll(&quot;hobby&quot;, Arrays.asList(hobby));
query.findObjects(new FindListener&lt;Person&gt;() {

    @Override
    public void done(List&lt;Person&gt; object,BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;查询成功：共&quot; + object.size() + &quot;条数据。&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage());
        }
    }

});
</code></pre>

<h2 id="_40">类名和表名的关系<a class="headerlink" href="#_40" title="Permanent link">&para;</a></h2>
<ul>
<li>Bmob官方推荐类名和表名完全一致的映射使用方式， 即如，上面的GameScore类，它在后台对应的表名也是GameScore（区分大小写）。</li>
<li>如果你希望表名和类名并不相同，如表名为T_a_b，而类名还是GameScore，那么你可以使用BmobObject提供的setTableName("表名")的方法，</li>
</ul>
<p>示例代码如下：</p>
<pre><code class="java">//这时候实际操作的表是T_a_b
public class GameScore extends BmobObject{
    private String playerName;
    private Integer score;
    private Boolean isPay;
    private BmobFile pic;

    public GameScore() {
        this.setTableName(&quot;T_a_b&quot;);
    }

    public String getPlayerName() {
        return playerName;
    }
    //其他方法，见上面的代码
}
</code></pre>

<p>当然了，除了在构造函数中直接调用setTableName方法之外，你还可以在GameScore的实例中动态调用setTableName方法。</p>
<h3 id="_41">查询自定义表名的数据<a class="headerlink" href="#_41" title="Permanent link">&para;</a></h3>
<p>如果您使用了setTableName方法来自定义表名，那么在对该表进行数据查询的时候必须使用以下方法。<code>需要注意的是查询的结果是JSONArray,需要自行解析JSONArray中的数据</code>。</p>
<pre><code class="java">/**
 * 查询数据
 */
public void queryData(){
    BmobQuery query =new BmobQuery(&quot;Person&quot;);
    query.addWhereEqualTo(&quot;age&quot;, 25);
    query.setLimit(2);
    query.order(&quot;createdAt&quot;);
    //v3.5.0版本提供`findObjectsByTable`方法查询自定义表名的数据
    query.findObjectsByTable(new QueryListener&lt;JSONArray&gt;() {
        @Override
        public void done(JSONArray ary, BmobException e) {
            if(e==null){
                Log.i(&quot;bmob&quot;,&quot;查询成功：&quot;+ary.toString());
            }else{
                Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage()+&quot;,&quot;+e.getErrorCode());
            }
        }
    });
}
</code></pre>

<p><strong>自定义表名情况下的更新、删除数据和普通的更新、删除数据方式一样，没有变化。为方便大家了解学习，我们提供了一个关于自定义表名情况下增删改查数据的Demo，下载地址是：<a href="https://github.com/bmob/bmob-android-demo-dynamic-tablename">https://github.com/bmob/bmob-android-demo-dynamic-tablename</a>。</strong></p>
<p><strong>自<code>V3.4.4</code>版本开始，SDK提供了另一种方法来更新数据，通过调用<code>Bmobobject</code>类中的<code>setValue（key，value）</code>方法，只需要传入key及想要更新的值即可</strong></p>
<p>举例，说明如下：</p>
<pre><code class="java">public class Person extends BmobObject {
    private BmobUser user;  //BmobObject类型
    private BankCard cards; //Object类型
    private Integer age;    //Integer类型
    private Boolean gender; //Boolean类型
    ...
    getter、setter方法
}

其中BankCard类结构如下：

public class BankCard{
    private String cardNumber;
    private String bankName;
    public BankCard(String bankName, String cardNumber){
        this.bankName = bankName;
        this.cardNumber = cardNumber;
    }
    ...
    getter、setter方法
}

</code></pre>

<pre><code class="java">Person p2=new Person();
//更新BmobObject的值
//  p2.setValue(&quot;user&quot;, BmobUser.getCurrentUser(this, MyUser.class));
//更新Object对象
p2.setValue(&quot;bankCard&quot;,new BankCard(&quot;农行&quot;, &quot;农行账号&quot;));
//更新Object对象的值
//p2.setValue(&quot;bankCard.bankName&quot;,&quot;建行&quot;);
//更新Integer类型
//p2.setValue(&quot;age&quot;,11);
//更新Boolean类型
//p2.setValue(&quot;gender&quot;, true);
p2.update(objectId, new UpdateListener() {

    @Override
    public void done(BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;更新成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;更新失败：&quot;+e.getMessage()+&quot;,&quot;+e.getErrorCode());
        }
    }

});

</code></pre>

<p><strong>注意：修改数据只能通过objectId来修改，目前不提供查询条件方式的修改方法。</strong></p>
<h3 id="_42">原子计数器<a class="headerlink" href="#_42" title="Permanent link">&para;</a></h3>
<p>很多应用可能会有计数器功能的需求，比如文章点赞的功能，如果大量用户并发操作，用普通的更新方法操作的话，会存在数据不一致的情况。</p>
<p>为此，Bmob提供了原子计数器来保证原子性的修改某一<strong>数值字段</strong>的值。注意：原子计数器只能对应用于Web后台的Number类型的字段，即JavaBeans数据对象中的Integer对象类型（<strong>不要用int类型</strong>）。</p>
<pre><code class="java">gameScore.increment(&quot;score&quot;); // 分数递增1
gameScore.update(updateListener);
</code></pre>

<p>您还可以通过<code>increment(key, amount)</code>方法来递增或递减任意幅度的数字</p>
<pre><code class="java">gameScore.increment(&quot;score&quot;, 5); // 分数递增5
//gameScore.increment(&quot;score&quot;, -5); // 分数递减5
gameScore.update(updateListener);
</code></pre>

<h3 id="_43">删除字段的值<a class="headerlink" href="#_43" title="Permanent link">&para;</a></h3>
<p>你可以在一个对象中删除一个字段的值，通过<code>remove</code>操作：</p>
<pre><code class="java">GameScore gameScore = new GameScore();
gameScore.setObjectId(&quot;dd8e6aff28&quot;);
gameScore.remove(&quot;score&quot;);  // 删除GameScore对象中的score字段
gameScore.update(new UpdateListener() {
    @Override
    public void done(BmobException e) {
        if(e==null){
            Log.i(&quot;bmob&quot;,&quot;成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;失败：&quot;+e.getMessage()+&quot;,&quot;+e.getErrorCode());
        }
    }
});
</code></pre>

<h1 id="7">7、图文消息<a class="headerlink" href="#7" title="Permanent link">&para;</a></h1>
<p>2017年下半年开始，后端云提供了素材管理模块，控制台文件浏览功能合并到了该模块下；</p>
<p><img alt="" src="https://i.imgur.com/Unl4dwy.png" /></p>
<h3 id="_44">适用场景<a class="headerlink" href="#_44" title="Permanent link">&para;</a></h3>
<ol>
<li>如果您的应用是需要展示很多图文消息或文章，可以用这里编辑来实现富文本信息的存储和编辑管理；</li>
<li>以往上传文件缺少了一些关联信息如文件描述之类的需要额外建表，来实现文件和描述信息的关联，这里可以一并解决；</li>
</ol>
<h3 id="_45">使用方法<a class="headerlink" href="#_45" title="Permanent link">&para;</a></h3>
<ol>
<li>后端控制台新建图文信息并编辑后会新增一个_Article表，表中的关键字段有url,title,content，分别代表图文信息网页的url地址如<a href="http://bmob-cdn-782.b0.upaiyun.com/2017/12/07/78d403d140b2c0af80c12b8d9de67a7f.html">此例</a>,标题和网页源码，也能实时编辑。</li>
<li>客户端的使用，可以查询_Article表，既可以拿到url用webview组件加载，也可以用Android SDK中的TextView结合Html类解析html标签并展示。</li>
</ol>
<pre><code>/**
 * 查询图文消息
 */
private void queryArticle() {
    BmobQuery&lt;BmobArticle&gt; bmobArticleBmobQuery = new BmobQuery&lt;&gt;();
    bmobArticleBmobQuery.findObjects(new FindListener&lt;BmobArticle&gt;() {
        @Override
        public void done(List&lt;BmobArticle&gt; object, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnQueryArticle, &quot;查询成功：&quot; + object.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Snackbar.make(mBtnQueryArticle, &quot;查询失败：&quot; + e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h1 id="8">8、文件管理<a class="headerlink" href="#8" title="Permanent link">&para;</a></h1>
<p><code>BmobFile</code>可以让你的应用程序将文件存储到服务器中，常见的文件类型都可以实现存储：比如图像文件、影像文件、音乐文件和任何其他二进制数据。</p>
<p><strong>注：</strong></p>
<p>1、以下均为SDK对文件进行操作的方法，如果你想在Web端对文件进行操作，请查看我们的<a href="http://doc.bmob.cn/other/common_problem/">帮助文档</a>中的<code>如何在Web后台上传文件</code>解答。</p>
<p>2、自 <code>BmobSDKv3.4.6</code> 开始，文件服务需要注意以下几个方面：</p>
<ul>
<li>
<p><strong>SDK内部集成CDN文件服务，删除<code>BmobProFile</code>的相关代码，并不再提供新旧文件管理的功能，但上传的方法名不变</strong>；</p>
</li>
<li>
<p><strong>新增了文件下载<code>(download)</code>和批量删除CDN文件<code>(deleteBatch)</code>的方法</strong>；</p>
</li>
<li>
<p><strong>2016年7月,旧版SDK中的新旧文件管理的上传方法将停止服务，之前通过旧版SDK中的新旧文件管理上传的文件仍可下载，请大家及时更新SDK</strong>；</p>
</li>
<li>
<p><strong>之前使用了<code>BmobProFile中</code>的<code>upload</code>方法上传的文件，开发者可以直接在文件的url地址后面增加："?t=2&amp;a="+ 你的accessKey，那么拼接后的文件是可以直接用来访问并下载的。</strong>；</p>
</li>
</ul>
<pre><code class="xml">    举个例子：

    如果之前通过新版文件管理的上传方法得到的文件url地址：
    http://newfile.codenow.cn:8080/a272a1aac5274f7085f140de9db94635.png，

    那么签名后的可访问的文件地址为：
    http://newfile.codenow.cn:8080/a272a1aac5274f7085f140de9db94635.png?t=2&amp;a=你的accessKey。
</code></pre>

<ul>
<li><strong>无法查看accessKey</strong>。因为已经废除新旧文件管理功能，所以在开发者管理后台的设置--&gt;应用密钥中已无法查看accessKey，而之前开发者所使用的accessKey继续有效。</li>
</ul>
<p>3、CDN文件服务需要<code>okhttp-2.4.0、okio-1.4.0</code>及<code>WAKE_LOCK</code>权限，请导入okhttp相关jar包并在<code>AndroidManifest.xml</code>类的<code>manifest</code>标签下添加如下权限，否则会造成调用上传/下载文件的方法无反应。</p>
<pre><code class="xml">    &lt;!--保持CPU 运转，屏幕和键盘灯有可能是关闭的,用于文件上传和下载 --&gt;
    &lt;uses-permission android:name=&quot;android.permission.WAKE_LOCK&quot; /&gt;
</code></pre>

<h3 id="_46">创建文件对象<a class="headerlink" href="#_46" title="Permanent link">&para;</a></h3>
<p>创建文件对象方式如下：</p>
<pre><code class="java">String picPath = &quot;sdcard/temp.jpg&quot;;
BmobFile bmobFile = new BmobFile(new File(picPath));
</code></pre>

<h3 id="_47">上传单一文件<a class="headerlink" href="#_47" title="Permanent link">&para;</a></h3>
<p>文件分片上传的方法非常简单，示例代码如下：</p>
<pre><code class="java">String picPath = &quot;sdcard/temp.jpg&quot;;
BmobFile bmobFile = new BmobFile(new File(picPath));
bmobFile.uploadblock(new UploadFileListener() {

    @Override
    public void done(BmobException e) {
        if(e==null){
            //bmobFile.getFileUrl()--返回的上传文件的完整地址
            toast(&quot;上传文件成功:&quot; + bmobFile.getFileUrl());
        }else{
            toast(&quot;上传文件失败：&quot; + e.getMessage());
        }

    }

    @Override
    public void onProgress(Integer value) {
        // 返回的上传进度（百分比）
    }
});
</code></pre>

<h4 id="_48">设置文件分片上传时每片大小<a class="headerlink" href="#_48" title="Permanent link">&para;</a></h4>
<p>自<code>BmobSDKv3.4.6</code>开始,新增<code>BmobConfig</code>类，允许开发者设置<code>查询超时时间</code>及<code>文件分片上传时的每片大小</code>。建议在<code>Application</code>类的<code>onCreate</code>方法中调用。</p>
<p>示例代码如下:</p>
<pre><code class="java">
public class BmobApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        //设置BmobConfig
        BmobConfig config =new BmobConfig.Builder()
        //请求超时时间（单位为秒）：默认15s
        .setConnectTimeout(30)
        //文件分片上传时每片的大小（单位字节），默认512*1024
        .setBlockSize(500*1024)
        .build();
        Bmob.getInstance().initConfig(config);
    }
}

</code></pre>

<h3 id="_49">批量上传文件<a class="headerlink" href="#_49" title="Permanent link">&para;</a></h3>
<p>自<code>BmobSDKv3.2.7</code>开始,新增批量上传文件的方法；</p>
<p>自<code>BmobSDKv3.4.6</code>开始,文件批量上传的静态方法由<code>Bmob</code>转移至<code>BmobFile</code>类,建议调用<code>BmobFile.uploadBatch</code>方法。</p>
<p>示例代码如下：</p>
<pre><code class="java">//详细示例可查看BmobExample工程中BmobFileActivity类
String filePath_mp3 = &quot;/mnt/sdcard/testbmob/test1.png&quot;;
String filePath_lrc = &quot;/mnt/sdcard/testbmob/test2.png&quot;;
final String[] filePaths = new String[2];
filePaths[0] = filePath_mp3;
filePaths[1] = filePath_lrc;
BmobFile.uploadBatch(filePaths, new UploadBatchListener() {

    @Override
    public void onSuccess(List&lt;BmobFile&gt; files,List&lt;String&gt; urls) {
        //1、files-上传完成后的BmobFile集合，是为了方便大家对其上传后的数据进行操作，例如你可以将该文件保存到表中
        //2、urls-上传文件的完整url地址
        if(urls.size()==filePaths.length){//如果数量相等，则代表文件全部上传完成
            //do something
        }
    }

    @Override
    public void onError(int statuscode, String errormsg) {
        ShowToast(&quot;错误码&quot;+statuscode +&quot;,错误描述：&quot;+errormsg);
    }

    @Override
    public void onProgress(int curIndex, int curPercent, int total,int totalPercent) {
        //1、curIndex--表示当前第几个文件正在上传
        //2、curPercent--表示当前上传文件的进度值（百分比）
        //3、total--表示总的上传文件数
        //4、totalPercent--表示总的上传进度（百分比）
    }
});
</code></pre>

<p><strong>注：</strong></p>
<p><strong>1、有多少个文件上传，onSuccess方法就会执行多少次;</strong></p>
<p><strong>2、通过onSuccess回调方法中的files或urls集合的大小与上传的总文件个数比较，如果一样，则表示全部文件上传成功。</strong></p>
<h3 id="_50">下载文件<a class="headerlink" href="#_50" title="Permanent link">&para;</a></h3>
<p>自<code>BmobSDKv3.4.6</code>版本,SDK提供了文件的下载方法<code>download</code>，并且允许开发者设置下载文件的存储路径。</p>
<p><strong>注：下载方法并不局限于下载通过BmobSDK上传的文件，也就是说只要提供一个文件url地址，也可以调用下载方法的。</strong></p>
<p>下载文件的步骤：</p>
<p>1、先获取BmobFile对象实例，可以是查询数据时返回的BmobFile，也可以自行构建BmobFile对象：</p>
<ul>
<li>通过查询数据时返回的BmobFile，示例代码如下：</li>
</ul>
<pre><code class="java">
bmobQuery.findObjects(new FindListener&lt;GameScore&gt;() {
    @Override
    public void done(List&lt;GameScore&gt; object,BmobException e) {
        if(e==null){
            for (GameScore gameScore : object) {
                BmobFile bmobfile = gameScore.getPic();
               if(file!= null){
                    //调用bmobfile.download方法
               }
            }
        }else{
            toast(&quot;查询失败：&quot;+e.getMessage());
        }
    }
});

</code></pre>

<ul>
<li>通过如下构造方法构造BmobFile对象：</li>
</ul>
<p>需求：如果你想下载一个远程图片地址，那么就需要使用下面的构造方法构造一个BmobFile对象（其中group可为空）</p>
<pre><code class="java">/**  
 * @param fileName 文件名(必填)
 * @param group 组名（选填）
 * @param url  完整url地址（必填）
 * 注：必须要有文件名和文件的完整url地址，group可为空
 */
public BmobFile(String fileName,String group,String url){
    this.filename = fileName;
    this.group=group;
    this.url = url;
}

</code></pre>

<p>示例代码如下：</p>
<pre><code class="java">
BmobFile bmobfile =new BmobFile(&quot;xxx.png&quot;,&quot;&quot;,&quot;http://bmob-cdn-2.b0.upaiyun.com/2016/04/12/58eeed852a7542cb964600c6cc0cd2d6.png&quot;)；

</code></pre>

<p>2、然后调用<code>bmobfile.download</code>方法下载文件:</p>
<p>有两种下载方法：</p>
<ul>
<li>
<p><code>download(DownloadFileListener listener)</code>：此方法会将文件下载到当前应用的默认缓存目录中，以getFilename()得到的值为文件名</p>
</li>
<li>
<p><code>download(File savePath, DownloadFileListener listener)</code>：此方法允许开发者指定文件存储目录和文件名</p>
</li>
</ul>
<p>示例代码如下：</p>
<pre><code class="java">private void downloadFile(BmobFile file){
    //允许设置下载文件的存储路径，默认下载文件的目录为：context.getApplicationContext().getCacheDir()+&quot;/bmob/&quot;
    File saveFile = new File(Environment.getExternalStorageDirectory(), file.getFilename());
    file.download(saveFile, new DownloadFileListener() {

        @Override
        public void onStart() {
            toast(&quot;开始下载...&quot;);
        }

        @Override
        public void done(String savePath,BmobException e) {
            if(e==null){
                toast(&quot;下载成功,保存路径:&quot;+savePath);
            }else{
                toast(&quot;下载失败：&quot;+e.getErrorCode()+&quot;,&quot;+e.getMessage());
            }
        }

        @Override
        public void onProgress(Integer value, long newworkSpeed) {
            Log.i(&quot;bmob&quot;,&quot;下载进度：&quot;+value+&quot;,&quot;+newworkSpeed);
        }

    });
}


</code></pre>

<h3 id="_51">删除文件<a class="headerlink" href="#_51" title="Permanent link">&para;</a></h3>
<p><code>BmobSDKv3.4.6</code>中删除文件的接口，<code>只能删除通过CDN文件服务（v3.4.6开始采用CDN文件服务）上传的文件</code>。不兼容之前的新旧文件管理，但使用方法不变。</p>
<p>示例代码如下：</p>
<pre><code class="java">BmobFile file = new BmobFile();
file.setUrl(url);//此url是上传文件成功之后通过bmobFile.getUrl()方法获取的。
file.delete(new UpdateListener() {

    @Override
    public void done(BmobException e) {
        if(e==null){
            toast(&quot;文件删除成功&quot;);
        }else{
            toast(&quot;文件删除失败：&quot;+e.getErrorCode()+&quot;,&quot;+e.getMessage());
        }
    }
});

</code></pre>

<h3 id="_52">批量删除文件<a class="headerlink" href="#_52" title="Permanent link">&para;</a></h3>
<p>自 <code>BmobSDKv3.4.6</code> 版本，SDK提供了文件的批量删除接口<code>deleteBatch，且只能删除通过CDN文件服务（v3.4.6开始采用CDN文件服务）上传的文件</code>。</p>
<p>示例代码如下：</p>
<pre><code class="java">//此url必须是上传文件成功之后通过bmobFile.getUrl()方法获取的。
String[] urls =new String[]{url};
BmobFile.deleteBatch(urls, new DeleteBatchListener() {

    @Override
    public void done(String[] failUrls, BmobException e) {
        if(e==null){
            toast(&quot;全部删除成功&quot;);
        }else{
            if(failUrls!=null){
                toast(&quot;删除失败个数：&quot;+failUrls.length+&quot;,&quot;+e.toString());
            }else{
                toast(&quot;全部文件删除失败：&quot;+e.getErrorCode()+&quot;,&quot;+e.toString());
            }
        }
    }
});

</code></pre>

<p>为方便大家理解文件服务的使用，Bmob提供了一个文件上传的案例和源码，大家可以到<a href="http://doc.bmob.cn/data/android/example/">示例和教程中查看和下载</a>。</p>
<h3 id="_53">缩略图<a class="headerlink" href="#_53" title="Permanent link">&para;</a></h3>
<p>自 <code>BmobSDKv3.4.6</code> 版本，新版文件服务由第三方厂商又拍云提供，只需要在图片上传成功返回的url后面拼接特定参数即可实现缩放，加水印等效果，<a href="http://bmob-cdn-9200.b0.upaiyun.com/2017/04/25/f24b9ef540f1aeb680ebe01ba8543d9f.png!/scale/80/watermark/text/5rC05Y2wCg==">如图</a>，具体可参考<a href="http://docs.upyun.com/cloud/image/">这里</a> 。</p>
<p><strong>注：</strong></p>
<p><strong>1、文件的批量上传是BmobSDK_v3.2.7版本才提供的功能，如需使用，请更新版本;</strong>
<strong>2、文件的下载和批量删除是BmobSDK_v3.4.6才提供的功能，如需使用，请更新版本。</strong></p>
<h1 id="9">9、数据监听<a class="headerlink" href="#9" title="Permanent link">&para;</a></h1>
<p><strong>数据监听按需收费，请开发者到【应用设置-套餐升级-数据监听】中开通此功能</strong></p>
<p>SDK可以实现对数据表或行的监听，当这个表或者行的数据发生变化时，Bmob会立即将变化的信息告知SDK。
这种服务非常适合做游戏开发（如，开发斗地主游戏，三个人同时监听一行数据的变化，任何一个人出牌都会将数据写入到这行数据中，其他人也就立即知道了）、群聊（一群人监听某个表的变化，任何人发言都会将数据写入到这个表中，其他人也可以立即知道了）等实时性要求很高的场景中。</p>
<p>为方便大家快速了解数据的实时同步服务，我们提供了一个简单的应用实例（ <a href="https://github.com/bmob/bmob-android-demo-realtime-data">https://github.com/bmob/bmob-android-demo-realtime-data</a> ）供大家参考。</p>
<h3 id="_54">开始连接<a class="headerlink" href="#_54" title="Permanent link">&para;</a></h3>
<p>使用数据监听功能，首先需要创建<code>BmobRealTimeData</code>对象,然后调用<code>start</code>方法连接服务器。</p>
<pre><code class="java">BmobRealTimeData rtd = new BmobRealTimeData();
rtd.start(new ValueEventListener() {
    @Override
    public void onDataChange(JSONObject data) {
        Log.d(&quot;bmob&quot;, &quot;(&quot;+data.optString(&quot;action&quot;)+&quot;)&quot;+&quot;数据：&quot;+data);
    }

    @Override
    public void onConnectCompleted(Exception ex) {
        Log.d(&quot;bmob&quot;, &quot;连接成功:&quot;+rtd.isConnected());
    }
});
</code></pre>

<p><code>start</code>方法中的<code>ValueEventListener</code>参数用于监听连接成功和数据变化的回调。当有数据变化时会通过onDataChange回调方法反馈到客户端。开发者只需要处理得到的data就可以了。</p>
<p><strong>注：</strong></p>
<p><strong>1、监听器不支持UI线程，在监听回调中请不要直接操作UI；</strong></p>
<p><strong>2、如果你要监听User、Installation等系统表的话，表名前需要加上“_”，例如：_User</strong></p>
<h3 id="_55">监听数据<a class="headerlink" href="#_55" title="Permanent link">&para;</a></h3>
<p>在BmobRealTimeData对象连接成功后，就可以进行数据的监听了。BmobSDK提供了监听表和行的方法如下：</p>
<pre><code class="java">// 监听表更新
rtd.subTableUpdate(tableName);
// 监听表删除
rtd.subTableDelete(tableName);
// 监听行更新
rtd.subRowUpdate(tableName, objectId);
// 监听行删除
rtd.subRowDelete(tableName, objectId);
</code></pre>

<p>其中<code>tableName</code>为要监听的数据表名，<code>objectId</code>为要监听的数据行Id,
通常比较保险的做法是在<code>BmobRealTimeData</code>对象的连接状态为<code>true</code>的情况下进行监听，代码如下：</p>
<pre><code class="java">if(rtd.isConnected()){
    // 监听表更新
    rtd.subTableUpdate(tableName);
}
</code></pre>

<h3 id="_56">取消监听<a class="headerlink" href="#_56" title="Permanent link">&para;</a></h3>
<p>当开发者想取消监听某个行为是，可使用下面的方法：</p>
<pre><code class="java">// 取消监听表更新
rtd.unsubTableUpdate(testTableName);
// 取消监听表删除
rtd.unsubTableDelete(testTableName);
// 取消监听行更新
rtd.unsubRowUpdate(testTableName, objectId);
// 取消监听行删除
rtd.unsubRowDelete(testTableName, objectId);
</code></pre>

<h1 id="10">10、数据安全<a class="headerlink" href="#10" title="Permanent link">&para;</a></h1>
<h2 id="acl">ACL和角色<a class="headerlink" href="#acl" title="Permanent link">&para;</a></h2>
<p>数据安全是软件系统中最重要的组成部分，为了更好的保护应用数据的安全，Bmob在软件架构层面提供了应用层次、表层次、ACL（Access Control List：访问控制列表）、角色管理（Role）四种不同粒度的权限控制的方式，确保用户数据的安全（详细请查看<a href="http://doc.bmob.cn/other/data_safety/">Bmob数据与安全页面</a>，了解Bmob如何保护数据安全）。</p>
<p>其中，最灵活的方法是通过ACL和角色，它的思路是每一条数据有一个用户和角色的列表，以及这些用户和角色拥有什么样的许可权限。</p>
<p>大多数应用程序需要对不同的数据进行灵活的访问和控制，这就可以使用Bmob提供的ACL模式来实现。例如：</p>
<ul>
<li>对于私有数据，读写权限可以只局限于数据的所有者。</li>
<li>对于一个论坛，会员和版主有写的权限，一般的游客只有读的权限。</li>
<li>对于日志数据只有开发者才能够访问，ACL可以拒绝所有的访问权限。</li>
<li>属于一个被授权的用户或者开发者所创建的数据，可以有公共的读的权限，但是写入权限仅限于管理者角色。</li>
<li>一个用户发送给另外一个用户的消息，可以只给这些用户赋予读写的权限。</li>
</ul>
<p>BmobACL和BmobUser的权限设置：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>setReadAccess(String userId, boolean allowed)</td>
<td>设置哪个用户是否可读</td>
</tr>
<tr>
<td>setReadAccess(BmobUser user, boolean allowed)</td>
<td>设置哪个用户是否可读</td>
</tr>
<tr>
<td>setWriteAccess(String userId, boolean allowed)</td>
<td>设置哪个用户是否可写</td>
</tr>
<tr>
<td>setWriteAccess(BmobUser user, boolean allowed)</td>
<td>设置哪个用户是否可写</td>
</tr>
</tbody>
</table>
<p>BmobACL和BmobRole的权限设置：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>setRoleReadAccess(String roleName, boolean allowed)</td>
<td>设置哪种角色是否可读</td>
</tr>
<tr>
<td>setRoleReadAccess(BmobRole role, boolean allowed)</td>
<td>设置哪种角色是否可读</td>
</tr>
<tr>
<td>setRoleWriteAccess(String roleName, boolean allowed)</td>
<td>设置哪种角色是否可写</td>
</tr>
<tr>
<td>setRoleWriteAccess(BmobRole role, boolean allowed)</td>
<td>设置哪种角色是否可写</td>
</tr>
</tbody>
</table>
<p>BmobACL和所有用户的权限设置：</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>setPublicReadAccess(boolean allowed)</td>
<td>设置所有用户是否可读</td>
</tr>
<tr>
<td>setPublicWriteAccess(boolean allowed)</td>
<td>设置所有用户是否可读</td>
</tr>
</tbody>
</table>
<h3 id="_57">默认访问权限<a class="headerlink" href="#_57" title="Permanent link">&para;</a></h3>
<p>在没有显示指定的情况下，每一个BmobObject(表)中的ACL(列)属性的默认值是所有人可读可写的。在客户端想要修改这个权限设置，只需要简单调用BmobACL的setPublicReadAccess方法和setPublicWriteAccess方法，即：</p>
<pre><code class="java">/**
 * 设置发布的帖子对所有用户的访问控制权限
 */
private void publicAcl() {
    User user = BmobUser.getCurrentUser(User.class);
    if (user == null) {
        Snackbar.make(mBtnAclPublic, &quot;请先登录&quot;, Snackbar.LENGTH_LONG).show();
    } else {
        Post post = new Post();
        post.setAuthor(user);
        post.setContent(&quot;content&quot; + System.currentTimeMillis());
        post.setTitle(&quot;title&quot; + System.currentTimeMillis());
        BmobACL bmobACL = new BmobACL();
        //设置此帖子为所有用户不可写
        bmobACL.setPublicWriteAccess(false);
        //设置此帖子为所有用户可读
        bmobACL.setPublicReadAccess(true);
        post.setACL(bmobACL);
        post.save(new SaveListener&lt;String&gt;() {
            @Override
            public void done(String s, BmobException e) {
                if (e == null) {
                    Snackbar.make(mBtnAclPublic, &quot;发布帖子成功&quot;, Snackbar.LENGTH_LONG).show();
                } else {
                    Snackbar.make(mBtnAclPublic, e.getMessage(), Snackbar.LENGTH_LONG).show();
                }
            }
        });
    }

}


</code></pre>

<p>注意：可读可写是默认的权限，不需要写额外的代码。</p>
<h3 id="_58">指定用户的访问权限<a class="headerlink" href="#_58" title="Permanent link">&para;</a></h3>
<p>假如你想实现一个分享日志类的应用时，这可能会需要针对不同的日志设定不同的访问权限。比如，公开的日志，发布者有更改和修改的权限，其他用户只有读的权限，那么可用如下代码实现：</p>
<pre><code class="java">/**
 * 设置发布的帖子对当前用户的访问控制权限
 */
private void userAcl() {
    User user = BmobUser.getCurrentUser(User.class);
    if (user == null) {
        Snackbar.make(mBtnAclPublic, &quot;请先登录&quot;, Snackbar.LENGTH_LONG).show();
    } else {
        Post post = new Post();
        post.setAuthor(user);
        post.setContent(&quot;content&quot; + System.currentTimeMillis());
        post.setTitle(&quot;title&quot; + System.currentTimeMillis());
        BmobACL bmobACL = new BmobACL();
        //设置此帖子为当前用户可写
        bmobACL.setReadAccess(user, true);
        //设置此帖子为所有用户可读
        bmobACL.setPublicReadAccess(true);
        post.setACL(bmobACL);
        post.save(new SaveListener&lt;String&gt;() {
            @Override
            public void done(String s, BmobException e) {
                if (e == null) {
                    Snackbar.make(mBtnAclPublic, &quot;发布帖子成功&quot;, Snackbar.LENGTH_LONG).show();
                } else {
                    Snackbar.make(mBtnAclPublic, e.getMessage(), Snackbar.LENGTH_LONG).show();
                }
            }
        });
    }
}



</code></pre>

<p>有时，用户想发表一篇不公开的日志，这种情况只有发布者才对这篇日志拥有读写权限，相应的代码如下：</p>
<pre><code class="java">Blog blog = new Blog();
blog.setTitle(&quot;一个人的秘密&quot;);
blog.setContent(&quot;这是blog的具体内容&quot;);

BmobACL acl = new BmobACL();  //创建ACL对象
acl.setReadAccess(BmobUser.getCurrentUser(), true); // 设置当前用户可写的权限
acl.setWriteAccess(BmobUser.getCurrentUser(), true); // 设置当前用户可写的权限

blog.setACL(acl);    //设置这条数据的ACL信息
blog.save(new SaveListener&lt;String&gt;() {

    @Override
    public void done(String objectId, BmobException e) {
        ...
    }
});
</code></pre>

<h3 id="_59">角色管理<a class="headerlink" href="#_59" title="Permanent link">&para;</a></h3>
<p>上面的指定用户访问权限虽然很方便，但是对于有些应用可能会有一定的局限性。比如一家公司的工资系统，员工和公司的出纳们只拥有工资的读权限，而公司的人事和老板才拥有全部的读写权限。要实现这种功能，你也可以通过设置每个用户的ACL权限来实现，如下：</p>
<pre><code class="java">/**
 * 设置发布的帖子对某种角色的访问控制权限
 */
private void roleAcl() {
    User user = BmobUser.getCurrentUser(User.class);
    if (user == null) {
        Snackbar.make(mBtnAclPublic, &quot;请先登录&quot;, Snackbar.LENGTH_LONG).show();
    } else {
        Post post = new Post();
        post.setAuthor(user);
        post.setContent(&quot;content&quot; + System.currentTimeMillis());
        post.setTitle(&quot;title&quot; + System.currentTimeMillis());
        BmobACL bmobACL = new BmobACL();
        //设置此帖子为当前用户可写
        bmobACL.setWriteAccess(user, true);
        //设置此帖子为某种角色可读
        bmobACL.setRoleReadAccess(&quot;female&quot;, true);
        post.setACL(bmobACL);
        post.save(new SaveListener&lt;String&gt;() {
            @Override
            public void done(String s, BmobException e) {
                if (e == null) {
                    Snackbar.make(mBtnAclPublic, &quot;发布帖子成功&quot;, Snackbar.LENGTH_LONG).show();
                } else {
                    Snackbar.make(mBtnAclPublic, e.getMessage(), Snackbar.LENGTH_LONG).show();
                }
            }
        });
    }
}

</code></pre>

<h3 id="_60">角色之间的从属关系<a class="headerlink" href="#_60" title="Permanent link">&para;</a></h3>
<p>下面我们来说一下角色与角色之间的从属关系。用一个例子来说明下：一个互联网企业有移动部门，部门中有不同的小组，如Android开发组和IOS开发组。每个小组只拥有自己小组的代码读写权限，但这两个小组同时拥有核心库代码的读权限。</p>
<pre><code class="java">/**
 * 查询某角色是否存在
 *
 * @param roleName
 */
private void queryRole(final String roleName) {
    BmobQuery&lt;BmobRole&gt; bmobRoleBmobQuery = new BmobQuery&lt;&gt;();
    bmobRoleBmobQuery.addWhereEqualTo(&quot;name&quot;, roleName);
    bmobRoleBmobQuery.findObjects(new FindListener&lt;BmobRole&gt;() {
        @Override
        public void done(List&lt;BmobRole&gt; list, BmobException e) {
            if (e == null) {
                if (list.size() &gt; 0) {
                    //已存在该角色
                    addUser2Role(list.get(0));
                } else {
                    //不存在该角色
                    BmobRole bmobRole = new BmobRole(roleName);
                    saveRoleAndAddUser2Role(bmobRole);
                }
            } else {
                Snackbar.make(mBtnQueryRole, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });

}
</code></pre>

<pre><code class="java">/**
 * 保存某个角色并保存用户到该角色中
 *
 * @param bmobRole
 */
private void saveRoleAndAddUser2Role(BmobRole bmobRole) {

    User user = BmobUser.getCurrentUser(User.class);
    if (user == null) {
        Snackbar.make(mBtnQueryRole, &quot;请先登录&quot;, Snackbar.LENGTH_LONG).show();
    } else {
        bmobRole.getUsers().add(user);
        bmobRole.save(new SaveListener&lt;String&gt;() {
            @Override
            public void done(String s, BmobException e) {
                if (e == null) {
                    Toast.makeText(BmobRoleActivity.this, &quot;角色用户添加成功&quot;, Toast.LENGTH_SHORT).show();
                } else {
                    Snackbar.make(mBtnQueryRole, e.getMessage(), Snackbar.LENGTH_LONG).show();
                }
            }
        });
    }

}
</code></pre>

<pre><code class="java">/**
 * 添加用户到某个角色中
 *
 * @param bmobRole
 */
private void addUser2Role(BmobRole bmobRole) {
    User user = BmobUser.getCurrentUser(User.class);
    if (user == null) {
        Snackbar.make(mBtnQueryRole, &quot;请先登录&quot;, Snackbar.LENGTH_LONG).show();
    } else {
        bmobRole.getUsers().add(user);
        bmobRole.update(new UpdateListener() {
            @Override
            public void done(BmobException e) {
                if (e == null) {
                    Toast.makeText(BmobRoleActivity.this, &quot;角色用户添加成功&quot;, Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(BmobRoleActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
}
</code></pre>

<pre><code class="java">/**
 * 把用户从某个角色中移除
 *
 * @param bmobRole
 */
private void removeUserFromRole(BmobRole bmobRole) {
    User user = BmobUser.getCurrentUser(User.class);
    if (user == null) {
        Snackbar.make(mBtnQueryRole, &quot;请先登录&quot;, Snackbar.LENGTH_LONG).show();
    } else {
        bmobRole.getUsers().remove(user);
        bmobRole.update(new UpdateListener() {
            @Override
            public void done(BmobException e) {
                if (e == null) {
                    Toast.makeText(BmobRoleActivity.this, &quot;角色用户添加成功&quot;, Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(BmobRoleActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
}
</code></pre>

<h3 id="acl_1">ACL案例源码<a class="headerlink" href="#acl_1" title="Permanent link">&para;</a></h3>
<p>我们为大家提供一个ACL相关的案例源码，大家可以点击下载：<a href="https://github.com/bmob/bmob-android-demo-acl">https://github.com/bmob/bmob-android-demo-acl</a></p>
<h2 id="_61">应用安全<a class="headerlink" href="#_61" title="Permanent link">&para;</a></h2>
<p>请大家在使用Bmob开发应用程序之前，仔细阅读<a href="http://doc.bmob.cn/other/data_safety/">数据与安全</a>的文档。</p>
<h1 id="11_1">11、地理位置<a class="headerlink" href="#11_1" title="Permanent link">&para;</a></h1>
<p>Bmob允许用户根据地球的经度和纬度坐标进行基于地理位置的信息查询。通过在BmobObject的查询中添加一个BmobGeoPoint的对象查询，你就可以实现轻松查找出离当前用户最接近的信息或地点的功能。</p>
<pre><code class="java">public class User extends BmobUser {
    /**
     * 用户当前位置
     */
    private BmobGeoPoint address;
}
</code></pre>

<h3 id="_62">创建地理位置对象<a class="headerlink" href="#_62" title="Permanent link">&para;</a></h3>
<p>首先需要创建一个BmobGeoPoint对象。例如，创建一个东经116.39727786183357度，北纬39.913768382429105度的BmobGeoPoint对象：</p>
<pre><code class="java">/**
 * 更新当前用户地理位置信息
 */
private void updateLocation() {
    //TODO 在实际应用中，此处利用实时定位替换为真实经纬度数据
    final BmobGeoPoint bmobGeoPoint = new BmobGeoPoint(116.39727786183357, 39.913768382429105);
    final User user = BmobUser.getCurrentUser(User.class);
    user.setAddress(bmobGeoPoint);
    user.update(new UpdateListener() {
        @Override
        public void done(BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnUpdateLocation, &quot;更新成功：&quot; + user.getAddress().getLatitude() + &quot;-&quot; + user.getAddress().getLongitude(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnUpdateLocation, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<h3 id="_63">查询地理位置信息<a class="headerlink" href="#_63" title="Permanent link">&para;</a></h3>
<pre><code class="java">/**
 * 获取当前用户的地理位置信息
 */
private void getLocation() {
    User user = BmobUser.getCurrentUser(User.class);
    if (user != null) {
        Snackbar.make(mBtnUpdateLocation, &quot;查询成功：&quot; + user.getAddress().getLatitude() + &quot;-&quot; + user.getAddress().getLongitude(), Snackbar.LENGTH_LONG).show();
    } else {
        Snackbar.make(mBtnUpdateLocation, &quot;请先登录&quot;, Snackbar.LENGTH_LONG).show();
    }
}
</code></pre>

<pre><code class="java">/**
 * 查询最接近某个坐标的用户
 */
private void queryNear() {
    BmobQuery&lt;User&gt; query = new BmobQuery&lt;&gt;();
    BmobGeoPoint location = new BmobGeoPoint(112.934755, 24.52065);
    query.addWhereNear(&quot;address&quot;, location);
    query.setLimit(10);
    query.findObjects(new FindListener&lt;User&gt;() {

        @Override
        public void done(List&lt;User&gt; users, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnUpdateLocation, &quot;查询成功：&quot; + users.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnUpdateLocation, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code class="java">/**
 * 查询指定坐标指定半径内的用户
 */
private void queryWithinRadians() {
    BmobQuery&lt;User&gt; query = new BmobQuery&lt;&gt;();
    BmobGeoPoint address = new BmobGeoPoint(112.934755, 24.52065);
    query.addWhereWithinRadians(&quot;address&quot;, address, 10.0);
    query.findObjects(new FindListener&lt;User&gt;() {

        @Override
        public void done(List&lt;User&gt; users, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnUpdateLocation, &quot;查询成功：&quot; + users.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnUpdateLocation, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code class="java">/**
 * 查询指定坐标指定英里范围内的用户
 */
private void queryWithinMiles() {
    BmobQuery&lt;User&gt; query = new BmobQuery&lt;&gt;();
    BmobGeoPoint address = new BmobGeoPoint(112.934755, 24.52065);
    query.addWhereWithinMiles(&quot;address&quot;, address, 10.0);
    query.findObjects(new FindListener&lt;User&gt;() {

        @Override
        public void done(List&lt;User&gt; users, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnUpdateLocation, &quot;查询成功：&quot; + users.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnUpdateLocation, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code class="java">/**
 * 查询指定坐标指定公里范围内的用户
 */
private void queryWithinKilometers() {
    BmobQuery&lt;User&gt; query = new BmobQuery&lt;&gt;();
    BmobGeoPoint address = new BmobGeoPoint(112.934755, 24.52065);
    query.addWhereWithinKilometers(&quot;address&quot;, address, 10);
    query.findObjects(new FindListener&lt;User&gt;() {

        @Override
        public void done(List&lt;User&gt; users, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnUpdateLocation, &quot;查询成功：&quot; + users.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnUpdateLocation, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<pre><code class="java">/**
 * 查询矩形范围内的用户
 */
private void queryBox() {
    BmobQuery&lt;User&gt; query = new BmobQuery&lt;&gt;();
    //TODO 西南点，矩形的左下角坐标
    BmobGeoPoint southwestOfSF = new BmobGeoPoint(112.934755, 24.52065);
    //TODO 东别点，矩形的右上角坐标
    BmobGeoPoint northeastOfSF = new BmobGeoPoint(116.627623, 40.143687);
    query.addWhereWithinGeoBox(&quot;address&quot;, southwestOfSF, northeastOfSF);
    query.findObjects(new FindListener&lt;User&gt;() {

        @Override
        public void done(List&lt;User&gt; users, BmobException e) {
            if (e == null) {
                Snackbar.make(mBtnUpdateLocation, &quot;查询成功：&quot; + users.size(), Snackbar.LENGTH_LONG).show();
            } else {
                Log.e(&quot;BMOB&quot;, e.toString());
                Snackbar.make(mBtnUpdateLocation, e.getMessage(), Snackbar.LENGTH_LONG).show();
            }
        }
    });
}
</code></pre>

<p><strong>注意事项</strong></p>
<ol>
<li>
<p><strong>每个BmobObject数据对象中只能有一个BmobGeoPoint对象</strong>。</p>
</li>
<li>
<p>地理位置的点不能超过规定的范围。<code>纬度的范围</code>应该是在<code>-90.0到90.0</code>之间。<code>经度的范围</code>应该是在<code>-180.0到180.0</code>之间。如果您添加的经纬度超出了以上范围，将导致程序错误。</p>
</li>
</ol>
<h1 id="12_1">12、其他功能<a class="headerlink" href="#12_1" title="Permanent link">&para;</a></h1>
<h3 id="_64">获取服务器时间<a class="headerlink" href="#_64" title="Permanent link">&para;</a></h3>
<p>在Bmob对象中提供了一个静态方法，用于获取服务器时间。</p>
<pre><code class="java">Bmob.getServerTime(new QueryListener&lt;Long&gt;() {

    @Override
    public void done(long time,BmobException e) {
        if(e==null){
            SimpleDateFormat formatter = new SimpleDateFormat(&quot;yyyy-MM-dd HH:mm&quot;);
            String times = formatter.format(new Date(time * 1000L));
            Log.i(&quot;bmob&quot;,&quot;当前服务器时间为:&quot; + times);
        }else{
            Log.i(&quot;bmob&quot;,&quot;获取服务器时间失败:&quot; + e.getMessage());
        }
    }

});
</code></pre>

<h3 id="_65">自动更新组件<a class="headerlink" href="#_65" title="Permanent link">&para;</a></h3>
<p>Bmob为大家提供了应用的自动更新组件，使用这个组件可以快速方便实现应用的自动升级功能。
详细的使用操作可以参考文档：<a href="http://doc.bmob.cn/data/android/auto_update/">自动更新组件文档</a></p>
<h3 id="_66">表结构<a class="headerlink" href="#_66" title="Permanent link">&para;</a></h3>
<p>自<code>V3.4.2</code>版本开始，SDK提供了<code>获取表结构信息</code>方法,具体示例如下：</p>
<h4 id="_67">获取特定表的结构<a class="headerlink" href="#_67" title="Permanent link">&para;</a></h4>
<pre><code class="java">Bmob.getTableSchema(&quot;待查询的表名&quot;, new QueryListener&lt;BmobTableSchema&gt;() {

    @Override
    public void done(BmobTableSchema schema, BmobException ex) {
        if(ex==null){
            Log.i(&quot;bmob&quot;, &quot;获取指定表的表结构信息成功：&quot;+schema.getClassName()+&quot;-&quot;+schema.getFields().toString());
        }else{
            Log.i(&quot;bmob&quot;, &quot;获取指定表的表结构信息失败:&quot; + ex.getLocalizedMessage()+&quot;(&quot;+ex.getErrorCode()+&quot;)&quot;);
        }
    }
});

</code></pre>

<h4 id="_68">获取所有表的结构<a class="headerlink" href="#_68" title="Permanent link">&para;</a></h4>
<pre><code class="java">
Bmob.getAllTableSchema(context, new QueryListListener&lt;BmobTableSchema&gt;() {

    @Override
    public void done(List&lt;BmobTableSchema&gt; schemas, BmobException ex) {
        if(ex==null &amp;&amp; schemas!=null &amp;&amp; schemas.size()&gt;0){
            Log.i(&quot;bmob&quot;, &quot;获取所有表结构信息成功&quot;);
        }else{
            Log.i(&quot;bmob&quot;,&quot;获取所有表结构信息失败：&quot;+ex.getLocalizedMessage()+&quot;(&quot;+ex.getErrorCode()+&quot;)&quot;);
        }
    }
});

</code></pre>

<h4 id="_69">返回数据说明<a class="headerlink" href="#_69" title="Permanent link">&para;</a></h4>
<p>注：<code>BmobTableSchema</code>参数说明：</p>
<p>其中</p>
<p><code>className</code>：表示表名</p>
<p><code>fields</code>   ： 是Map<String, Map<String,Object>&gt;类型，里面包含了对应表的所有列的属性，</p>
<p>其fields内部结构如下：</p>
<blockquote>
<p>{"列1":Map,"列2":Map, ...}</p>
</blockquote>
<p>而Map的结构为：</p>
<blockquote>
<p>{"type":"typeName","targetClass":"tableName"}</p>
</blockquote>
<p>其中 <code>type</code> 指的是该列的类型， 而 <code>targetClass</code> 指的是指向的表名，只有在 type 为 Pointer 或者 Relation 时才有值。</p>
<p>具体json格式如下,仅供参考：</p>
<pre><code class="java"> {
    className: &quot;Post&quot;,
    fields: {
      ACL: {
        type: &quot;Object&quot;
      },
      author: {
        targetClass: &quot;_User&quot;,
        type: &quot;Pointer&quot;
      },
      content: {
        type: &quot;String&quot;
      },
      createdAt: {
        type: &quot;Date&quot;
      },
      objectId: {
        type: &quot;String&quot;
      },
      updatedAt: {
        type: &quot;Date&quot;
      }
    }
 }

</code></pre>

<h1 id="13sdk">13、SDK错误码列表<a class="headerlink" href="#13sdk" title="Permanent link">&para;</a></h1>
<p>Android SDK的错误码都是以<code>9</code>开头的，其他错误码请点击查看：<a href="http://doc.bmob.cn/other/error_code/">错误码文档</a>。</p>
<table>
<thead>
<tr>
<th>错误码</th>
<th>内容</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>9001</td>
<td>AppKey is Null, Please initialize BmobSDK.</td>
<td>Application Id为空，请初始化。</td>
</tr>
<tr>
<td>9002</td>
<td>Parse data error</td>
<td>解析返回数据出错</td>
</tr>
<tr>
<td>9003</td>
<td>upload file error</td>
<td>上传文件出错</td>
</tr>
<tr>
<td>9004</td>
<td>upload file failure</td>
<td>文件上传失败</td>
</tr>
<tr>
<td>9005</td>
<td>A batch operation can not be more than 50</td>
<td>批量操作只支持最多50条</td>
</tr>
<tr>
<td>9006</td>
<td>objectId is null</td>
<td>objectId为空</td>
</tr>
<tr>
<td>9007</td>
<td>BmobFile File size must be less than 10M.</td>
<td>文件大小超过10M</td>
</tr>
<tr>
<td>9008</td>
<td>BmobFile File does not exist.</td>
<td>上传文件不存在</td>
</tr>
<tr>
<td>9009</td>
<td>No cache data.</td>
<td>没有缓存数据</td>
</tr>
<tr>
<td>9010</td>
<td>The network is not normal.(Time out)</td>
<td>网络超时</td>
</tr>
<tr>
<td>9011</td>
<td>BmobUser does not support batch operations.</td>
<td>BmobUser类不支持批量操作</td>
</tr>
<tr>
<td>9012</td>
<td>context is null.</td>
<td>上下文为空</td>
</tr>
<tr>
<td>9013</td>
<td>BmobObject Object names(database table name) format is not correct.</td>
<td>BmobObject（数据表名称）格式不正确</td>
</tr>
<tr>
<td>9014</td>
<td></td>
<td>第三方账号授权失败</td>
</tr>
<tr>
<td>9015</td>
<td></td>
<td>其他错误均返回此code</td>
</tr>
<tr>
<td>9016</td>
<td>The network is not available,please check your network!</td>
<td>无网络连接，请检查您的手机网络。</td>
</tr>
<tr>
<td>9017</td>
<td></td>
<td>与第三方登录有关的错误，具体请看对应的错误描述</td>
</tr>
<tr>
<td>9018</td>
<td></td>
<td>参数不能为空</td>
</tr>
<tr>
<td>9019</td>
<td></td>
<td>格式不正确：手机号码、邮箱地址、验证码</td>
</tr>
</tbody>
</table>
<h1 id="14_1">14、混淆打包<a class="headerlink" href="#14_1" title="Permanent link">&para;</a></h1>
<p>使用了BmobSDK的应用在混淆过程中，需注意以下几点：</p>
<p>1、<code>不要混淆BmobSDK的代码</code>，Bmob Android SDK本身进行了代码混淆；</p>
<p>2、任何继承自<code>BmobObject、BmobUser</code>的JavaBean及<code>在上述JavaBean中定义的Object属性类</code>都不要混淆，否则gson将无法将数据解析成具体对象；</p>
<p>3、确保<code>rx</code>、<code>okhttp3 okio</code>、<code>gson</code>及<code>org.apache.http.legacy.jar</code>包均不要混淆。</p>
<p>具体可参考BmobExample中proguard-project.txt的代码：</p>
<pre><code class="xml">
-ignorewarnings

-keepattributes Signature,*Annotation*

# keep BmobSDK
-dontwarn cn.bmob.v3.**
-keep class cn.bmob.v3.** {*;}

# 确保JavaBean不被混淆-否则gson将无法将数据解析成具体对象
-keep class * extends cn.bmob.v3.BmobObject {
    *;
}
-keep class com.example.bmobexample.bean.BankCard{*;}
-keep class com.example.bmobexample.bean.GameScore{*;}
-keep class com.example.bmobexample.bean.MyUser{*;}
-keep class com.example.bmobexample.bean.Person{*;}
-keep class com.example.bmobexample.file.Movie{*;}
-keep class com.example.bmobexample.file.Song{*;}
-keep class com.example.bmobexample.relation.Post{*;}
-keep class com.example.bmobexample.relation.Comment{*;}

# keep BmobPush
-dontwarn  cn.bmob.push.**
-keep class cn.bmob.push.** {*;}

# keep okhttp3、okio
-dontwarn okhttp3.**
-keep class okhttp3.** { *;}
-keep interface okhttp3.** { *; }
-dontwarn okio.**

# keep rx
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
 long producerIndex;
 long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
 rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
 rx.internal.util.atomic.LinkedQueueNode consumerNode;
}

# 如果你需要兼容6.0系统，请不要混淆org.apache.http.legacy.jar
-dontwarn android.net.compatibility.**
-dontwarn android.net.http.**
-dontwarn com.android.internal.http.multipart.**
-dontwarn org.apache.commons.**
-dontwarn org.apache.http.**
-keep class android.net.compatibility.**{*;}
-keep class android.net.http.**{*;}
-keep class com.android.internal.http.multipart.**{*;}
-keep class org.apache.commons.**{*;}
-keep class org.apache.http.**{*;}

</code></pre>

<h1 id="15_1">15、其他功能<a class="headerlink" href="#15_1" title="Permanent link">&para;</a></h1>
<h2 id="_70">模板代码<a class="headerlink" href="#_70" title="Permanent link">&para;</a></h2>
<p>在使用SDK过程中，如果一些Api如查询是高频代码，可以把一些重复的样板代码抽出来，并在AndroidStudio中设置模板，即可实现快速输入，能提高编码效率，效果如下：</p>
<p><img alt="" src="http://i.imgur.com/zjm4Avx.gif" /></p>
<h2 id="_71">重置域名<a class="headerlink" href="#_71" title="Permanent link">&para;</a></h2>
<p>从v3.6.7开始，数据服务SDK新增了能重新设置请求域名的API，需要在初始化SDK前调用：</p>
<pre><code class="Java">Bmob.resetDomain(&quot;http://open-vip.bmob.cn/8/&quot;);
</code></pre>

<p>其中，参数为开发者的域名，调用后的所有请求都指向新的域名。</p>
<pre><code class="Java">http://open-vip.bmob.cn/8/
此域名目前仅为企业版用户使用！
</code></pre>

<h2 id="_72">数据迁移<a class="headerlink" href="#_72" title="Permanent link">&para;</a></h2>
<p>在应用设置-套餐升级-应用套餐类型，购买了企业Pro版的用户，可以提交工单通知工作人员进行数据迁移。</p>
<h2 id="_73">海外加速<a class="headerlink" href="#_73" title="Permanent link">&para;</a></h2>
<p>在应用设置-套餐升级，购买了海外节点加速功能的用户，可以提高海外访问速度。</p>
<h2 id="_74">统计功能<a class="headerlink" href="#_74" title="Permanent link">&para;</a></h2>
<p>从v3.5.2开始，数据服务SDK新增了统计功能。
从v3.6.0开始，数据服务SDK移除了统计功能。</p>
<ul>
<li>
<p>应用权限</p>
<pre><code>&lt;uses-permission android:name="android.permission.INTERNET" /&gt;
&lt;uses-permission android:name="android.permission.READ_PHONE_STATE" /&gt;
</code></pre>
</li>
<li>
<p>渠道设置</p>
<pre><code>Bmob.initialize(this,APPID,"BMOB");
</code></pre>
</li>
</ul>
<h1 id="16_1">16、版本兼容<a class="headerlink" href="#16_1" title="Permanent link">&para;</a></h1>
<h3 id="android-60">Android 6.0<a class="headerlink" href="#android-60" title="Permanent link">&para;</a></h3>
<ul>
<li>添加对Apache的HTTP-client支持
Android6.0版本开始移除了对Apache的HTTP Client的支持，需要在<code>app</code>的<code>build.gradle</code>文件添加配置:</li>
</ul>
<pre><code class="gradle">android {
    useLibrary 'org.apache.http.legacy'
}
</code></pre>

<h3 id="android-p">Android P 网络配置<a class="headerlink" href="#android-p" title="Permanent link">&para;</a></h3>
<p>在 res 下新建一个 xml 目录，然后创建一个名为 network_security_config.xml 文件 ，该文件内容如下：</p>
<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;network-security-config&gt;
    &lt;base-config cleartextTrafficPermitted=&quot;true&quot; /&gt;
&lt;/network-security-config&gt;
</code></pre>

<p>然后在 AndroidManifest.xml application 标签内应用上面的xml配置：</p>
<pre><code>    &lt;application
        android:networkSecurityConfig=&quot;@xml/network_security_config&quot;&gt;
    &lt;/application&gt;
</code></pre>
                </div>
            </div>
        </div>

        
            <script>var base_url = '../../..';</script>
            <script src="../../../js/jquery-1.10.2.min.js"></script>
            <script src="../../../js/bootstrap-3.0.3.min.js"></script>
            <script src="../../../js/highlight.pack.js"></script>
            <script src="../../../js/main.js"></script>
            <script src="../../../js/base.js"></script>

        <div id="go-top"><i class="fa fa-chevron-up"></i></div>
        <a href="https://docs.bmob.cn/data/Android/a_faststart/doc/index.html" id="back" target="_blank">返回 <br> 旧版</a>
    </body>

</html>